on master help sign in
344 files changed+5107−2230
since splitsnapshots
unifiedsplit
.buildkite/pipeline.yml+18 .circleci/config.yml−49 .github/workflows/binaries.yml+39 .github/workflows/build.sh+7 .gitignore+1 CHANGELOG.md+214 CONTRIBUTING.md+1−1 DESIGN.md+11−11 README.md+57−13 app/Main.hs+133−95 data/examples/declaration/annotation/annotation-out.hs+1 data/examples/declaration/class/associated-data1-out.hs−1 data/examples/declaration/class/associated-data2-out.hs−1 data/examples/declaration/class/associated-type-defaults-out.hs−2 data/examples/declaration/class/associated-types1-out.hs−1 data/examples/declaration/class/associated-types2-out.hs−1 data/examples/declaration/class/default-implementations-comments-out.hs−1 data/examples/declaration/class/default-signatures-out.hs−2 data/examples/declaration/class/default-signatures-simple-out.hs−2 data/examples/declaration/class/dependency-super-classes-out.hs+2−4 data/examples/declaration/class/functional-dependencies-out.hs+2−1 data/examples/declaration/class/multi-parameters2-out.hs+2−3 data/examples/declaration/class/newlines-after-where-out.hs+5 data/examples/declaration/class/newlines-after-where.hs+7 data/examples/declaration/class/newlines-and-default-decls-out.hs+11 data/examples/declaration/class/newlines-and-default-decls.hs+13 data/examples/declaration/class/newlines-and-haddocks-out.hs+23 data/examples/declaration/class/newlines-and-haddocks.hs+19 data/examples/declaration/class/newlines-between-methods-out.hs+17 data/examples/declaration/class/newlines-between-methods.hs+15 data/examples/declaration/class/single-parameters-out.hs−2 data/examples/declaration/class/type-operators3-out.hs+1−1 data/examples/declaration/data/existential-multiline-out.hs+12 data/examples/declaration/data/existential-multiline.hs+7 data/examples/declaration/data/field-layout/gadt-out.hs+2−1 data/examples/declaration/data/field-layout/record-out.hs+9−9 data/examples/declaration/data/gadt/multiline-out.hs+1−1 data/examples/declaration/data/gadt/multiline-where-out.hs+7 data/examples/declaration/data/gadt/multiline-where.hs+6 data/examples/declaration/data/multiline-arg-parens-out.hs+1−1 data/examples/declaration/data/partly-documented-out.hs+5 data/examples/declaration/data/partly-documented.hs+2 data/examples/declaration/data/record-multi-const-out.hs+17 data/examples/declaration/data/record-multi-const.hs+13 data/examples/declaration/data/record-out.hs+19−20 data/examples/declaration/data/simple-broken-out.hs+1−2 data/examples/declaration/data/unnamed-field-comment-0-out.hs+7 data/examples/declaration/data/unnamed-field-comment-0.hs+5 data/examples/declaration/data/unnamed-field-comment-1-out.hs+5 data/examples/declaration/data/unnamed-field-comment-1.hs+4 data/examples/declaration/data/with-comment-out.hs+4 data/examples/declaration/data/with-comment.hs+5 data/examples/declaration/data/with-weird-haddock-out.hs+4 data/examples/declaration/data/with-weird-haddock.hs+2 data/examples/declaration/instance/associated-data-out.hs−2 data/examples/declaration/instance/associated-data.hs+1 data/examples/declaration/instance/associated-types-out.hs−1 data/examples/declaration/instance/associated-types.hs+2 data/examples/declaration/instance/data-family-instances-gadt-out.hs+4−2 data/examples/declaration/instance/data-family-instances-newtype-out.hs+3−4 data/examples/declaration/instance/instance-sigs-multiple-out.hs−2 data/examples/declaration/instance/multi-parameter-out.hs−2 data/examples/declaration/instance/newlines-after-where-out.hs+5 data/examples/declaration/instance/newlines-after-where.hs+7 data/examples/declaration/instance/newlines-between-methods-out.hs+18 data/examples/declaration/instance/newlines-between-methods.hs+16 data/examples/declaration/instance/single-parameter-out.hs−2 data/examples/declaration/signature/minimal/minimal-out.hs−2 data/examples/declaration/signature/specialize/specialize-instance-out.hs−4 data/examples/declaration/signature/standalone-kind-out.hs+13 data/examples/declaration/signature/standalone-kind.hs+13 data/examples/declaration/signature/type/infix-promoted-type-constructor-out.hs−1 data/examples/declaration/signature/type/multi-value-out.hs+5 data/examples/declaration/signature/type/multi-value.hs+5 data/examples/declaration/signature/type/unrelated-out.hs+4 data/examples/declaration/signature/type/unrelated.hs+4 data/examples/declaration/splice/grouped-splices-out.hs+3 data/examples/declaration/splice/grouped-splices.hs+3 data/examples/declaration/splice/splice-decl.hs−1 data/examples/declaration/type-families/closed-type-family/multi-line-out.hs+6−5 data/examples/declaration/type-synonyms/with-weird-haddock-out.hs+3 data/examples/declaration/type-synonyms/with-weird-haddock.hs+1 data/examples/declaration/type/visible-forall-out.hs+2 data/examples/declaration/type/visible-forall.hs+2 data/examples/declaration/value/function/application-out.hs → data/examples/declaration/value/function/application-0-out.hs data/examples/declaration/value/function/application.hs → data/examples/declaration/value/function/application-0.hs data/examples/declaration/value/function/application-1-out.hs+19 data/examples/declaration/value/function/application-1.hs+14 data/examples/declaration/value/function/arg-breakpoints-out.hs+3 data/examples/declaration/value/function/arg-breakpoints.hs+3 data/examples/declaration/value/function/arrow/expression-applications-out.hs−9 data/examples/declaration/value/function/arrow/expression-applications.hs−9 data/examples/declaration/value/function/arrow/expression-forms-out.hs−14 data/examples/declaration/value/function/arrow/expression-forms.hs−11 data/examples/declaration/value/function/arrow/multiline-case-out.hs+8 data/examples/declaration/value/function/arrow/multiline-case.hs+7 data/examples/declaration/value/function/arrow/proc-applications-out.hs+6−5 data/examples/declaration/value/function/arrow/proc-do-complex-out.hs+18−14 data/examples/declaration/value/function/arrow/proc-do-simple1-out.hs+3−2 data/examples/declaration/value/function/arrow/proc-do-simple2-out.hs+9−7 data/examples/declaration/value/function/arrow/proc-forms1-out.hs+8−6 data/examples/declaration/value/function/arrow/proc-forms2-out.hs+10−6 data/examples/declaration/value/function/arrow/proc-forms2.hs+6−6 data/examples/declaration/value/function/arrow/proc-ifs-out.hs+6−4 data/examples/declaration/value/function/arrow/proc-parentheses-out.hs+5−4 data/examples/declaration/value/function/arrow/recursive-procs-out.hs+14−10 data/examples/declaration/value/function/awkward-comment-0-out.hs+6 data/examples/declaration/value/function/awkward-comment-0.hs+5 data/examples/declaration/value/function/awkward-comment-1-out.hs+9 data/examples/declaration/value/function/awkward-comment-1.hs+7 data/examples/declaration/value/function/blank-lines-let-out.hs+6 data/examples/declaration/value/function/blank-lines-let.hs+9 data/examples/declaration/value/function/blank-lines-where-out.hs+6 data/examples/declaration/value/function/blank-lines-where.hs+7 data/examples/declaration/value/function/block-arguments-out.hs+5 data/examples/declaration/value/function/block-arguments.hs+5 data/examples/declaration/value/function/do/blocks-out.hs+1 data/examples/declaration/value/function/do/comment-alignment-out.hs+9 data/examples/declaration/value/function/do/comment-alignment.hs+9 data/examples/declaration/value/function/do/comment-spacing-out.hs+7 data/examples/declaration/value/function/do/comment-spacing.hs+7 data/examples/declaration/value/function/do/recursive-do-rec-out.hs+5 data/examples/declaration/value/function/if-with-comment-out.hs+8 data/examples/declaration/value/function/if-with-comment.hs+8 data/examples/declaration/value/function/implicit-params-out.hs+1 data/examples/declaration/value/function/implicit-params.hs+2 data/examples/declaration/value/function/infix/dollar-chains-out.hs+7−3 data/examples/declaration/value/function/infix/dollar-chains.hs+4 data/examples/declaration/value/function/infix/hanging-out.hs+4−3 data/examples/declaration/value/function/infix/lenses-out.hs+8−7 data/examples/declaration/value/function/let-single-line-out.hs−1 data/examples/declaration/value/function/list-notation-0-out.hs+5 data/examples/declaration/value/function/list-notation-0.hs+5 data/examples/declaration/value/function/list-notation-1-out.hs+8 data/examples/declaration/value/function/list-notation-1.hs+7 data/examples/declaration/value/function/list-notation-2-out.hs+6 data/examples/declaration/value/function/list-notation-2.hs+5 data/examples/declaration/value/function/list-notation-3-out.hs+12 data/examples/declaration/value/function/list-notation-3.hs+10 data/examples/declaration/value/function/multi-way-if-out.hs+10−4 data/examples/declaration/value/function/multi-way-if.hs+5 data/examples/declaration/value/function/operator-comments-2-out.hs+5 data/examples/declaration/value/function/operator-comments-2.hs+4 data/examples/declaration/value/function/operators-0-out.hs+5 data/examples/declaration/value/function/operators-0.hs+4 data/examples/declaration/value/function/operators-1-out.hs+4 data/examples/declaration/value/function/operators-1.hs+3 data/examples/declaration/value/function/operators-2-out.hs+4 data/examples/declaration/value/function/operators-2.hs+4 data/examples/declaration/value/function/operators-3-out.hs+3 data/examples/declaration/value/function/operators-3.hs+3 data/examples/declaration/value/function/operators-4-out.hs+3 data/examples/declaration/value/function/operators-4.hs+3 data/examples/declaration/value/function/operators-5-out.hs+5 data/examples/declaration/value/function/operators-5.hs+4 data/examples/declaration/value/function/operators-6-out.hs+9 data/examples/declaration/value/function/operators-6.hs+7 data/examples/declaration/value/function/overindentation-out.hs+11−9 data/examples/declaration/value/function/pattern/famous-cardano-pattern-out.hs+8−8 data/examples/declaration/value/function/pattern/multiline-case-pattern-out.hs+2−2 data/examples/declaration/value/function/pattern/view-pattern-out.hs−3 data/examples/declaration/value/function/pattern/view-pattern.hs−3 data/examples/declaration/value/function/record-constructors-out.hs−27 data/examples/declaration/value/function/record/constructors-out.hs+31 data/examples/declaration/value/function/record-constructors.hs → data/examples/declaration/value/function/record/constructors.hs data/examples/declaration/value/function/record/dot-out.hs+15 data/examples/declaration/value/function/record/dot.hs+14 data/examples/declaration/value/function/record/hanging-after-equal-con-out.hs+8 data/examples/declaration/value/function/record/hanging-after-equal-con.hs+7 data/examples/declaration/value/function/record/hanging-after-equal-update-out.hs+8 data/examples/declaration/value/function/record/hanging-after-equal-update.hs+7 data/examples/declaration/value/function/record/inter-comments-out.hs+13 data/examples/declaration/value/function/record/inter-comments.hs+11 data/examples/declaration/value/function/record-updaters-out.hs → data/examples/declaration/value/function/record/updaters-out.hs data/examples/declaration/value/function/record-updaters.hs → data/examples/declaration/value/function/record/updaters.hs data/examples/declaration/value/function/record-wildcards-out.hs → data/examples/declaration/value/function/record/wildcards-out.hs+7−6 data/examples/declaration/value/function/record-wildcards.hs → data/examples/declaration/value/function/record/wildcards.hs data/examples/declaration/value/function/type-applications-and-splice-out.hs+4 data/examples/declaration/value/function/type-applications-and-splice.hs+4 data/examples/declaration/value/function/type-applications-out.hs+6 data/examples/declaration/value/function/type-applications.hs+5 data/examples/import/deduplication-bug-out.hs+6 data/examples/import/deduplication-bug.hs+6 data/examples/import/explicit-imports-out.hs+4−4 data/examples/import/explicit-imports-with-comments-out.hs+3−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/misc-out.hs−6 data/examples/import/nested-explicit-imports-out.hs+6−6 data/examples/import/qualified-post-out.hs+5 data/examples/import/qualified-post.hs+5 data/examples/import/qualified-prelude-out.hs+1−1 data/examples/import/simple-out.hs+2−3 data/examples/import/simple.hs+2 data/examples/import/sorted-export-list-out.hs+1 data/examples/import/sorted-export-list.hs+1 data/examples/module-header/double-dot-with-names-out.hs+1−6 data/examples/module-header/multiline2.hs+2−2 data/examples/module-header/preceding-comment-with-haddock-out.hs+1−4 data/examples/module-header/shebang-out.hs+1 data/examples/module-header/shebang-with-pragmas-out.hs+5 data/examples/module-header/shebang-with-pragmas.hs+5 data/examples/module-header/shebang.hs+1 data/examples/module-header/singleline-empty-out.hs+1−4 data/examples/module-header/stack-header-0-out.hs+7 data/examples/module-header/stack-header-0.hs+4 data/examples/module-header/stack-header-1-out.hs+8 data/examples/module-header/stack-header-1.hs+5 data/examples/module-header/stack-header-2-out.hs+12 data/examples/module-header/stack-header-2.hs+12 data/examples/module-header/warning-pragma-multiline-out.hs+1−4 data/examples/module-header/warning-pragma-out.hs+3−1 data/examples/other/argument-comment-out.hs−1 data/examples/other/comment-after-preceding-haddock-out.hs+12 data/examples/other/comment-after-preceding-haddock.hs+12 data/examples/other/comment-alignment-out.hs−1 data/examples/other/comment-before-hanging-out.hs+5 data/examples/other/comment-before-hanging.hs+5 data/examples/other/comment-glued-together-out.hs+7 data/examples/other/comment-glued-together.hs+6 data/examples/other/comment-trailing-space-out.hs+9 data/examples/other/comment-trailing-space.hs+9 data/examples/other/comment-two-blocks-out.hs+2−2 data/examples/other/cpp/continuation-out.hs+8 data/examples/other/cpp/continuation.hs+8 data/examples/other/cpp/lonely-hash-out.hs+14 data/examples/other/cpp/lonely-hash.hs+14 data/examples/other/cpp/separation-0a-out.hs+7 data/examples/other/cpp/separation-0a.hs+6 data/examples/other/cpp/separation-0b-out.hs+10 data/examples/other/cpp/separation-0b.hs+9 data/examples/other/cpp/separation-1a-out.hs+12 data/examples/other/cpp/separation-1a.hs+11 data/examples/other/cpp/separation-1b-out.hs+15 data/examples/other/cpp/separation-1b.hs+14 data/examples/other/cpp/separation-2a-out.hs+9 data/examples/other/cpp/separation-2a.hs+8 data/examples/other/cpp/separation-2b-out.hs+11 data/examples/other/cpp/separation-2b.hs+11 data/examples/other/cpp/shifted-out.hs+21 data/examples/other/cpp/shifted.hs+17 data/examples/other/cpp/simple-import-out.hs+14 data/examples/other/cpp/simple-import.hs+14 data/examples/other/disabling/indented-fragment-out.hs+7 data/examples/other/disabling/indented-fragment.hs+7 data/examples/other/disabling/single-definition-out.hs+10 data/examples/other/disabling/single-definition.hs+10 data/examples/other/disabling/whole-file-out.hs+10 data/examples/other/disabling/whole-file.hs+10 data/examples/other/empty-forall-out.hs+18 data/examples/other/empty-forall.hs+17 data/examples/other/inline-comment-0-out.hs+3 data/examples/other/inline-comment-0.hs+3 data/examples/other/inline-comment-1-out.hs+12 data/examples/other/inline-comment-1.hs+9 data/examples/other/multiline-forall-out.hs+1−2 data/examples/other/multiple-blank-line-comment-out.hs+9 data/examples/other/multiple-blank-line-comment.hs+10 data/examples/other/pragma-comments-after-out.hs+4 data/examples/other/pragma-comments-after.hs+3 data/examples/other/pragma-comments-out.hs+8 data/examples/other/pragma-comments.hs+11 data/examples/other/pragma-out.hs+1−4 data/examples/other/pragma-sorting-out.hs+11 data/examples/other/pragma-sorting.hs+10 default.nix+108−50 expected-failures/Agda.txt+11 expected-failures/aws.txt+9 expected-failures/distributed-process.txt+7 expected-failures/esqueleto.txt+7 expected-failures/fay.txt+18 expected-failures/hlint.txt+11 expected-failures/idris.txt+7 expected-failures/intero.txt+5 expected-failures/leksah.txt+5 expected-failures/lens.txt+3 expected-failures/pandoc.txt+9 expected-failures/pipes.txt+3 expected-failures/postgrest.txt+5 expected-failures/purescript.txt+18 expected-failures/tensorflow.txt+7 format.sh+6−3 nix/nixpkgs/default.nix+7−3 nix/ormolize/default.nix+29−13 nix/ormolize/ormolize.sh−17 ormolu.cabal+174−155 region-tests/expected-result-17-18.hs+17 region-tests/expected-result-6-7.hs+17 region-tests/expected-result-6-8.hs+16 region-tests/expected-result-9-12.hs+17 region-tests/expected-result-all.hs+21 region-tests/src.hs+17 shell.nix+4−2 src/GHC.hs+2−13 src/GHC/DynFlags.hs+33−20 src/Ormolu.hs+19−19 src/Ormolu/Config.hs+65−29 src/Ormolu/Diff.hs+25−17 src/Ormolu/Exception.hs+24−19 src/Ormolu/Imports.hs+172−51 src/Ormolu/Parser.hs+99−68 src/Ormolu/Parser/CommentStream.hs+152−64 src/Ormolu/Parser/Pragma.hs+5−5 src/Ormolu/Parser/Result.hs+26−11 src/Ormolu/Parser/Shebang.hs+27 src/Ormolu/Printer.hs+17−7 src/Ormolu/Printer/Combinators.hs+39−54 src/Ormolu/Printer/Comments.hs+69−78 src/Ormolu/Printer/Internal.hs+173−109 src/Ormolu/Printer/Meat/Common.hs+20−18 src/Ormolu/Printer/Meat/Declaration.hs+175−110 src/Ormolu/Printer/Meat/Declaration.hs-boot+2−3 src/Ormolu/Printer/Meat/Declaration/Annotation.hs+2−3 src/Ormolu/Printer/Meat/Declaration/Class.hs+26−38 src/Ormolu/Printer/Meat/Declaration/Data.hs+74−47 src/Ormolu/Printer/Meat/Declaration/Default.hs+4−5 src/Ormolu/Printer/Meat/Declaration/Foreign.hs+2−3 src/Ormolu/Printer/Meat/Declaration/Instance.hs+21−27 src/Ormolu/Printer/Meat/Declaration/RoleAnnotation.hs+2−6 src/Ormolu/Printer/Meat/Declaration/Rule.hs+21−16 src/Ormolu/Printer/Meat/Declaration/Signature.hs+49−33 src/Ormolu/Printer/Meat/Declaration/Splice.hs+2−3 src/Ormolu/Printer/Meat/Declaration/Type.hs+8−8 src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs+27−28 src/Ormolu/Printer/Meat/Declaration/Value.hs+435−357 src/Ormolu/Printer/Meat/Declaration/Value.hs-boot−4 src/Ormolu/Printer/Meat/Declaration/Warning.hs+8−10 src/Ormolu/Printer/Meat/ImportExport.hs+45−36 src/Ormolu/Printer/Meat/Module.hs+35−15 src/Ormolu/Printer/Meat/Pragma.hs+67−13 src/Ormolu/Printer/Meat/Type.hs+93−68 src/Ormolu/Printer/Operators.hs+57−62 src/Ormolu/Printer/SpanStream.hs+1−1 src/Ormolu/Processing/Common.hs+27 src/Ormolu/Processing/Cpp.hs+70 src/Ormolu/Processing/Postprocess.hs+31 src/Ormolu/Processing/Preprocess.hs+149 src/Ormolu/Utils.hs+83−9 tests/Ormolu/Parser/PragmaSpec.hs+1−1 tests/Ormolu/PrinterSpec.hs+7−10
.buildkite/pipeline.ymlnew file+18
@@ -0,0 +1,18 @@
1+steps:
2+ - label: Build and test with GHC 8.6.5
3+ command: |
4+ nix-build --keep-going --no-out-link --argstr ormoluCompiler ghc865
5+ timeout: 100
6+ - label: Build and test with GHC 8.8.4
7+ command: |
8+ nix-build --keep-going --no-out-link --argstr ormoluCompiler ghc884
9+ timeout: 100
10+ - label: Build and test with GHC 8.10.1
11+ command: |
12+ nix-build --keep-going --no-out-link --argstr ormoluCompiler ghc8101
13+ timeout: 100
14+ - wait
15+ - label: Check formatting
16+ command: |
17+ ./format.sh
18+ git diff --exit-code --color=always
.circleci/config.ymldeleted−49
@@ -1,49 +0,0 @@
1−version: 2
2−
3−jobs:
4− build:
5− docker:
6− - image: nixos/nix:2.1.3
7− steps:
8− - checkout
9− - run:
10− name: Add ca-certificates
11− command: |
12− apk --no-progress update
13− apk --no-progress add ca-certificates
14− - run:
15− name: Setup Cachix
16− command: |
17− nix-env -iA cachix -f https://cachix.org/api/v1/install
18− USER=dummy cachix use tweag
19− - run:
20− name: Build environment
21− command: |
22− nix-shell --pure --run :
23− - run:
24− name: Push Cachix
25− command: |
26− if [ -n "$CACHIX_SIGNING_KEY" ];
27− then nix-store -qR --include-outputs $(nix-instantiate default.nix) | cachix push tweag;
28− fi
29− - run:
30− name: Build the package (includes running the tests)
31− command: nix-build --keep-going
32− - run:
33− name: Check formatting
34− command: |
35− ./format.sh
36− nix-shell -p "(import ./nix/nixpkgs/default.nix).git" --command "git diff --exit-code --color=always"
37− - run:
38− name: Generate Haddocks
39− command: nix-build --attr ormolu.doc
40− - store_artifacts:
41− path: result-doc/share/doc
42− destination: haddock
43−
44−workflows:
45− version: 2
46− build:
47− jobs:
48− - build:
49− context: org-global
.github/workflows/binaries.ymlnew file+39
@@ -0,0 +1,39 @@
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.shnew file+7
@@ -0,0 +1,7 @@
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
.gitignore+1
@@ -26,4 +26,5 @@ cabal.sandbox.config
2626dist-*/
2727dist/
2828stack.yaml
29+stack.yaml.lock
2930result*
CHANGELOG.md+214
@@ -1,10 +1,198 @@
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).
24+
25+* The magic comments for disabling and enabling Ormolu now can encompass any
26+ fragment of code provided that the remaining code after exclusion of the
27+ disabled part is still syntactically correct. [Issue
28+ 601](https://github.com/tweag/ormolu/issues/601).
29+
30+* Improved sorting of operators in imports. [Issue
31+ 602](https://github.com/tweag/ormolu/issues/602).
32+
33+* Fixed a bug related to trailing space in multiline comments in certain
34+ cases. [Issue 603](https://github.com/tweag/ormolu/issues/602).
35+
36+* Added support for formatting linked lists with `(:)` as line terminator.
37+ [Issue 478](https://github.com/tweag/ormolu/issues/478).
38+
39+* Fixed rendering of function arguments in multiline layout. [Issue
40+ 609](https://github.com/tweag/ormolu/issues/609).
41+
42+* Blank lines between definitions in `let` and `while` bindings are now
43+ preserved. [Issue 554](https://github.com/tweag/ormolu/issues/554).
44+
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).
47+
48+* Improved region formatting so that indented fragments—such as definitions
49+ inside of `where` clauses—can be formatted. [Issue
50+ 572](https://github.com/tweag/ormolu/issues/572).
51+
52+* Fixed the bug related to the de-association of pragma comments. [Issue
53+ 619](https://github.com/tweag/ormolu/issues/619).
54+
55+## Ormolu 0.1.0.0
56+
57+* Fixed rendering of type signatures concerning several identifiers. [Issue
58+ 566](https://github.com/tweag/ormolu/issues/566).
59+
60+* Fixed an idempotence issue with inline comments in tuples and parentheses.
61+ [Issue 450](https://github.com/tweag/ormolu/issues/450).
62+
63+* Fixed an idempotence issue when certain comments were picked up as
64+ “continuation” of a series of comments [Issue
65+ 449](https://github.com/tweag/ormolu/issues/449).
66+
67+* Fixed an idempotence issue related to different indentation levels in a
68+ comment series. [Issue 512](https://github.com/tweag/ormolu/issues/512).
69+
70+* Fixed an idempotence issue related to comments which may happen to be
71+ separated from the elements they are attached to by the equality sign.
72+ [Issue 340](https://github.com/tweag/ormolu/issues/340).
73+
74+* Fixed an idempotence issue with type synonym and data declarations where
75+ the type has a Haddock. [Issue
76+ 578](https://github.com/tweag/ormolu/issues/578).
77+
78+* Fix the false positive about AST differences in presence of comments with
79+ multiple blank lines in a row. [Issue
80+ 518](https://github.com/tweag/ormolu/issues/518).
81+
82+* Fixed rendering of comments around if expressions. [Issue
83+ 458](https://github.com/tweag/ormolu/issues/458).
84+
85+* Unnamed fields of data constructors are now documented using the `-- ^`
86+ syntax. [Issue 445](https://github.com/tweag/ormolu/issues/445) and [Issue
87+ 428](https://github.com/tweag/ormolu/issues/428).
88+
89+* Fixed non-idempotent transformation of partly documented data definition.
90+ [Issue 590](https://github.com/tweag/ormolu/issues/590).
91+
92+* Fixed an idempotence issue related to operators. [Issue
93+ 522](https://github.com/tweag/ormolu/issues/522).
94+
95+* Renamed the `--check-idempotency` flag to `--check-idempotence`.
96+ Apparently only the latter is correct.
97+
98+## Ormolu 0.0.5.0
99+
100+* Grouping of statements in `do`-blocks is now preserved. [Issue
101+ 74](https://github.com/tweag/ormolu/issues/74).
102+
103+* Grouping of TH splices is now preserved. [Issue
104+ 507](https://github.com/tweag/ormolu/issues/507).
105+
106+* Comments on pragmas are now preserved. [Issue
107+ 216](https://github.com/tweag/ormolu/issues/216).
108+
109+* Ormolu can now be enabled and disabled via special comments. [Issue
110+ 435](https://github.com/tweag/ormolu/issues/435).
111+
112+* Added experimental support for simple CPP. [Issue
113+ 415](https://github.com/tweag/ormolu/issues/415).
114+
115+* Added two new options `--start-line` and `--end-line` that allow us to
116+ select a region to format. [Issue
117+ 516](https://github.com/tweag/ormolu/issues/516).
118+
119+* Fixed rendering of module headers in the presence of preceding comments or
120+ Haddocks. [Issue 561](https://github.com/tweag/ormolu/issues/561).
121+
122+## Ormolu 0.0.4.0
123+
124+* When given several files to format, Ormolu does not stop on the first
125+ failure, but tries to format all the files. [Issue
126+ 502](https://github.com/tweag/ormolu/issues/502).
127+
128+* Made rendering of pattern matches involving operators consistent with
129+ other cases of rendering of operators. [Issue
130+ 500](https://github.com/tweag/ormolu/issues/500).
131+
132+* More compact rendering of type family injectivity constraints. [Issue
133+ 482](https://github.com/tweag/ormolu/issues/482).
134+
135+* Improved rendering of the keyword `where` in class, data, and type family
136+ declarations. When headers of these declarations are multi-line `where` is
137+ now put on its own line. [Issue
138+ 509](https://github.com/tweag/ormolu/issues/509).
139+
140+* Fixed the bug pertaining to rendering of arrow notation with multiline
141+ expressions. [Issue 513](https://github.com/tweag/ormolu/issues/513).
142+
143+* Made rendering of data type definitions, value-level applications, and
144+ application of types use the same style. Moreover, existential now doesn't
145+ cause the data constructor be unconditionally rendered in multiline
146+ layout. [Issue 427](https://github.com/tweag/ormolu/issues/427).
147+
148+* Records with a single data constructor are now formatted more compactly.
149+ [Issue 425](https://github.com/tweag/ormolu/issues/425).
150+
151+* Switched to `ghc-lib-parser-8.10.1`.
152+
153+* Implemented support for the new language extensions `ImportQualifiedPost`
154+ and `StandaloneKindSignatures`.
155+
156+## Ormolu 0.0.3.1
157+
158+* Fixed rendering of record updates with the record dot preprocessor syntax
159+ [Issue 498](https://github.com/tweag/ormolu/issues/498).
160+
161+## Ormolu 0.0.3.0
162+
163+* Fixed an issue related to unnecessary use of curly braces. [Issue
164+ 473](https://github.com/tweag/ormolu/issues/473).
165+
166+* Fixed the issue with formatting multi-way if when it happens to be a
167+ function applied to arguments [Issue
168+ 488](https://github.com/tweag/ormolu/issues/488). This changed the way
169+ multi-line if is formatted in general.
170+
171+* Added support for record dot pre-processor when used via the plugin.
172+ [Issue 486](https://github.com/tweag/ormolu/issues/486).
173+
174+* Stopped hanging record constructors and improved placing
175+ potentially-hanging consturctions in the presence of comments. [Issue
176+ 447](https://github.com/tweag/ormolu/issues/447).
177+
178+* Fixed indentation in presence of type applications. [Issue
179+ 493](https://github.com/tweag/ormolu/issues/493).
180+
181+* Class and instance declarations now do not have a blank line after
182+ `where`. Grouping of methods from the original input is also preserved
183+ with some normalizations. [Issue
184+ 431](https://github.com/tweag/ormolu/issues/431).
185+
186+## Ormolu 0.0.2.0
187+
3188* Switched to `ghc-lib-parser` instead of depending on the `ghc` package
4189 directly. This should allow us to use newest features of GHC while not
5190 necessarily depending on the newest version of the compiler. In addition
6191 to that Ormolu is now GHCJS-compatible.
7192
193+* Now unrecognized GHC options passed with `--ghc-opt` cause Ormolu to fail
194+ (exit code 7).
195+
8196* Fixed formatting of result type in closed type families. See [issue
9197 420](https://github.com/tweag/ormolu/issues/420).
10198
@@ -15,6 +203,32 @@
15203 inside the export list. See [issue
16204 430](https://github.com/tweag/ormolu/issues/430).
17205
206+* Empty `forall`s are now correctly preserved. See [issue
207+ 429](https://github.com/tweag/ormolu/issues/429).
208+
209+* Fixed [issue 446](https://github.com/tweag/ormolu/issues/446), which
210+ involved braces and operators.
211+
212+* When there are comments between preceding Haddock (pipe-style) and its
213+ corresponding declaration they are preserved like this in the output
214+ instead of being shifted. To be clear, this is not a very good idea to
215+ have comments in that position because the Haddock will end up not being
216+ associated with the declarations. Issues
217+ [440](https://github.com/tweag/ormolu/issues/440) and
218+ [448](https://github.com/tweag/ormolu/issues/448).
219+
220+* Implemented correct handling of shebangs. [Issue
221+ 377](https://github.com/tweag/ormolu/issues/377).
222+
223+* Implemented correct handling of stack headers. [Issue
224+ 393](https://github.com/tweag/ormolu/issues/393).
225+
226+* Sorting language pragmas cannot not change meaning of the input program
227+ anymore. [Issue 404](https://github.com/tweag/ormolu/issues/404).
228+
229+* Fixed formatting of applications where function is a complex expression.
230+ [Issue 444](https://github.com/tweag/ormolu/issues/444).
231+
18232## Ormolu 0.0.1.0
19233
20234* Initial release.
CONTRIBUTING.md+1−1
@@ -30,7 +30,7 @@ Testing is performed as following:
3030* The result of printing is parsed back again and the AST is compared to the
3131 AST obtained from the original file. They should match.
3232* The output of printer is checked against the expected output.
33−* Idempotency property is verified: formatting already formatted code
33+* Idempotence property is verified: formatting already formatted code
3434 results in exactly the same output.
3535
3636Examples can be organized in sub-directories, see the existing ones for
DESIGN.md+11−11
@@ -24,7 +24,7 @@ It also includes recommendations for future implementers.
2424
2525We set for the following goals (mostly taken from
2626[brittany](https://github.com/lspitzner/brittany)):
27−* Preserve the meaning of the formatted functions;
27+* Preserve the meaning of the formatted functions when no CPP is used;
2828* Make reasonable use of screen space;
2929* Use linear space and computation time on the size of the input;
3030* Preserve comments;
@@ -140,8 +140,10 @@ impossible to guess what they'll be called on.
140140
141141### CPP
142142
143−Formatting a module which uses CPP directives won't be supported. Instead,
144−we hope for a solution to replace CPP to do conditional compilation.
143+We allow CPP directives in the input, but we forgo the goal to preserve the
144+meaning of the formatted functions in that case.
145+Instead of supporting CPP better, we hope for a solution to replace CPP to
146+do conditional compilation.
145147
146148There are the following challenges when formatting a module with CPP:
147149
@@ -180,14 +182,15 @@ True
180182```
181183
182184At the time of this writing, formatting this program with Hindent
183−produces the same output we would get if the CPP directives were
184−considered comments:
185+or Ormolu produces the same output we would get if the CPP directives
186+were considered comments:
185187
186188```
187−$ hindent --version
188−hindent 5.2.7
189+$ ormolu --version
190+ormolu 0.0.5.0 HEAD fc64eada5c4da7a5b07d2872e253671b48aec115
191+using ghc-lib-parser 8.10.1.20200412
189192
190−$ hindent test.hs
193+$ ormolu --mode inplace test.hs
191194
192195$ cat test.hs
193196{-# LANGUAGE CPP #-}
@@ -227,9 +230,6 @@ to parse Haskell modules. If CPP is replaced with some language
227230extension or mechanism to do conditional compilation, all tools
228231will benefit from it.
229232
230−Therefore, CPP won't be supported. If the CPP extension
231−is enabled, we should signal an error right away.
232−
233233### Printing
234234
235235Just pretty-printing code (following the approach of Hindent) seems sane. It
README.md+57−13
@@ -4,7 +4,7 @@
44[![Hackage](https://img.shields.io/hackage/v/ormolu.svg?style=flat)](https://hackage.haskell.org/package/ormolu)
55[![Stackage Nightly](http://stackage.org/package/ormolu/badge/nightly)](http://stackage.org/nightly/package/ormolu)
66[![Stackage LTS](http://stackage.org/package/ormolu/badge/lts)](http://stackage.org/lts/package/ormolu)
7−[![CircleCI](https://circleci.com/gh/tweag/ormolu/tree/master.svg?style=svg&circle-token=cfd37a39265561eb44e608f97cf953cb2a394c03)](https://circleci.com/gh/tweag/ormolu/tree/master)
7+[![Build status](https://badge.buildkite.com/8e3b0951f3652b77e1c422b361904136a539b0522029156354.svg?branch=master)](https://buildkite.com/tweag-1/ormolu)
88
99Ormolu is a formatter for Haskell source code. The project was created with
1010the following goals in mind:
@@ -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 accomodate them.
25+ extensions enter broad use, we may change the style to accommodate 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,11 +45,12 @@ Alternatively, `stack` could be used with a `stack.yaml` file as follows.
4545
4646```console
4747$ cat stack.yaml
48−resolver: lts-14.3
48+resolver: lts-16.0
4949packages:
5050- '.'
5151
52−$ stack build
52+$ stack build # to build
53+$ stack install # to install
5354```
5455
5556To use Ormolu directly from GitHub with Nix, this snippet may come in handy:
@@ -91,20 +92,44 @@ formatted output.
9192$ ormolu --mode inplace Module.hs
9293```
9394
95+Use `find` to format a tree recursively:
96+
97+```console
98+$ ormolu --mode inplace $(find . -name '*.hs')
99+```
100+
101+## Magic comments
102+
103+Ormolu understands two magic comments:
104+
105+```haskell
106+{- ORMOLU_DISABLE -}
107+```
108+
109+and
110+
111+```haskell
112+{- ORMOLU_ENABLE -}
113+```
114+
115+This allows us to disable formatting selectively for code between these
116+markers or disable it for the entire file. To achieve the latter, just put
117+`{- ORMOLU_DISABLE -}` at the very top. Note that the source code should
118+still be parseable even without the “excluded” part. Because of that the
119+magic comments cannot be placed arbitrary, but should rather enclose
120+independent top-level definitions.
121+
94122## Current limitations
95123
96−* Does not handle CPP (wontfix, see [the design document][design]).
124+* CPP support is experimental. CPP is virtually impossible to handle
125+ correctly, so we process them as a sort of unchangeable snippets. This
126+ works only in simple cases when CPP conditionals surround top-level
127+ declarations. See the [CPP][design-cpp] section in the design notes for
128+ a discussion of the dangers.
97129* Input modules should be parsable by Haddock, which is a bit stricter
98130 criterion than just being valid Haskell modules.
99131* Various minor idempotence issues, most of them are related to comments.
100132
101−## Editor integration
102−
103−We know of the following editor integrations:
104−
105−* [Emacs][emacs-package]
106−* [VS Code][vs-code-plugin]
107−
108133## Running on Hackage
109134
110135It's possible to try Ormolu on arbitrary packages from Hackage. For that
@@ -119,6 +144,22 @@ also contain formatted `.hs` files for inspection and original inputs with
119144`.hs-original` extension (those are with CPP dropped, exactly what is fed
120145into Ormolu).
121146
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+
122163## Contributing
123164
124165See [CONTRIBUTING.md][contributing].
@@ -129,9 +170,12 @@ See [LICENSE.md][license].
129170
130171Copyright © 2018–present Tweag I/O
131172
132−[design]: https://github.com/tweag/ormolu/blob/master/DESIGN.md#cpp
173+[design-cpp]: https://github.com/tweag/ormolu/blob/master/DESIGN.md#cpp
133174[contributing]: https://github.com/tweag/ormolu/blob/master/CONTRIBUTING.md
134175[license]: https://github.com/tweag/ormolu/blob/master/LICENSE.md
135176[haskell-src-exts]: https://hackage.haskell.org/package/haskell-src-exts
136177[emacs-package]: https://github.com/vyorkin/ormolu.el
137178[vs-code-plugin]: https://marketplace.visualstudio.com/items?itemName=sjurmillidahl.ormolu-vscode
179+[vim-ormolu]: https://github.com/sdiehl/vim-ormolu
180+[neoformat]: https://github.com/sbdchd/neoformat
181+[aur]: https://aur.archlinux.org/packages/ormolu
app/Main.hs+133−95
@@ -1,12 +1,17 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE LambdaCase #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE LambdaCase #-}
33{-# LANGUAGE RecordWildCards #-}
44{-# LANGUAGE TemplateHaskell #-}
5+{-# LANGUAGE TypeApplications #-}
56
67module Main (main) where
78
9+import Control.Exception (SomeException, displayException, try)
810import Control.Monad
11+import Data.Bool (bool)
12+import Data.Either (lefts)
913import Data.List (intercalate, sort)
14+import qualified Data.Text.IO as TIO
1015import Data.Version (showVersion)
1116import Development.GitRev
1217import Options.Applicative
@@ -16,10 +21,8 @@ import Ormolu.Utils (showOutputable)
1621import Paths_ormolu (version)
1722import System.Exit (ExitCode (..), exitWith)
1823import System.IO (hPutStrLn, stderr)
19−import qualified Data.Text.IO as TIO
2024
2125-- | Entry point of the program.
22−
2326main :: IO ()
2427main = withPrettyOrmoluExceptions $ do
2528 Opts {..} <- execParser optsParserInfo
@@ -27,35 +30,50 @@ main = withPrettyOrmoluExceptions $ do
2730 case optInputFiles of
2831 [] -> formatOne' Nothing
2932 ["-"] -> formatOne' Nothing
30− xs -> mapM_ (formatOne' . Just) xs
33+ [x] -> formatOne' (Just x)
34+ xs -> do
35+ -- It is possible to get IOException, error's and 'OrmoluException's
36+ -- from 'formatOne', so we just catch everything.
37+ errs <-
38+ lefts <$> mapM (try @SomeException . formatOne' . Just) (sort xs)
39+ unless (null errs) $ do
40+ mapM_ (hPutStrLn stderr . displayException) errs
41+ exitWith (ExitFailure 102)
3142
3243-- | Format a single input.
33−
34−formatOne
35− :: Mode -- ^ Mode of operation
36− -> Config -- ^ Configuration
37− -> Maybe FilePath -- ^ File to format or stdin as 'Nothing'
38− -> IO ()
44+formatOne ::
45+ -- | Mode of operation
46+ Mode ->
47+ -- | Configuration
48+ Config RegionIndices ->
49+ -- | File to format or stdin as 'Nothing'
50+ Maybe FilePath ->
51+ IO ()
3952formatOne mode config = \case
4053 Nothing -> do
4154 r <- ormoluStdin config
4255 case mode of
4356 Stdout -> TIO.putStr r
44− _ -> do
45− hPutStrLn stderr
57+ _ -> do
58+ hPutStrLn
59+ stderr
4660 "This feature is not supported when input comes from stdin."
47− -- 101 is different from all the other exit codes we already use.
61+ -- 101 is different from all the other exit codes we already use.
4862 exitWith (ExitFailure 101)
4963 Just inputFile -> do
50− r <- ormoluFile config inputFile
64+ originalInput <- TIO.readFile inputFile
65+ formattedInput <- ormoluFile config inputFile
5166 case mode of
5267 Stdout ->
53− TIO.putStr r
54− InPlace ->
55− TIO.writeFile inputFile r
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
5675 Check -> do
57− r' <- TIO.readFile inputFile
58− when (r /= r') $
76+ when (formattedInput /= originalInput) $
5977 -- 100 is different to all the other exit code that are emitted
6078 -- either from an 'OrmoluException' or from 'error' and
6179 -- 'notImplemented'.
@@ -65,101 +83,121 @@ formatOne mode config = \case
6583-- Command line options parsing.
6684
6785data Opts = Opts
68− { optMode :: !Mode
69− -- ^ Mode of operation
70− , optConfig :: !Config
71− -- ^ Ormolu 'Config'
72− , optInputFiles :: ![FilePath]
73− -- ^ Haskell source files to format or stdin (when the list is empty)
86+ { -- | Mode of operation
87+ optMode :: !Mode,
88+ -- | Ormolu 'Config'
89+ optConfig :: !(Config RegionIndices),
90+ -- | Haskell source files to format or stdin (when the list is empty)
91+ optInputFiles :: ![FilePath]
7492 }
7593
7694-- | Mode of operation.
77−
7895data Mode
79− = Stdout -- ^ Output formatted source code to stdout
80− | InPlace -- ^ Overwrite original file
81− | Check -- ^ Exit with non-zero status code if
82− -- source is not already formatted
96+ = -- | Output formatted source code to stdout
97+ Stdout
98+ | -- | Overwrite original file
99+ InPlace
100+ | -- | Exit with non-zero status code if
101+ -- source is not already formatted
102+ Check
83103 deriving (Eq, Show)
84104
85105optsParserInfo :: ParserInfo Opts
86−optsParserInfo = info (helper <*> ver <*> exts <*> optsParser) . mconcat $
87− [ fullDesc
88− , progDesc ""
89− , header ""
90− ]
106+optsParserInfo =
107+ info (helper <*> ver <*> exts <*> optsParser) . mconcat $
108+ [ fullDesc,
109+ progDesc "",
110+ header ""
111+ ]
91112 where
92113 ver :: Parser (a -> a)
93− ver = infoOption verStr . mconcat $
94− [ long "version"
95− , short 'v'
96− , help "Print version of the program"
97− ]
98− verStr = intercalate "\n"
99− [ unwords
100− [ "ormolu"
101− , showVersion version
102− , $gitBranch
103− , $gitHash
114+ ver =
115+ infoOption verStr . mconcat $
116+ [ long "version",
117+ short 'v',
118+ help "Print version of the program"
119+ ]
120+ verStr =
121+ intercalate
122+ "\n"
123+ [ unwords
124+ [ "ormolu",
125+ showVersion version,
126+ $gitBranch,
127+ $gitHash
128+ ],
129+ "using ghc-lib-parser " ++ VERSION_ghc_lib_parser
104130 ]
105− , "using ghc-lib-parser " ++ VERSION_ghc_lib_parser
106− ]
107131 exts :: Parser (a -> a)
108− exts = infoOption displayExts . mconcat $
109− [ long "manual-exts"
110− , help "Display extensions that need to be enabled manually"
111− ]
132+ exts =
133+ infoOption displayExts . mconcat $
134+ [ long "manual-exts",
135+ help "Display extensions that need to be enabled manually"
136+ ]
112137 displayExts = unlines $ sort (showOutputable <$> manualExts)
113138
114139optsParser :: Parser Opts
115−optsParser = Opts
116− <$> (option parseMode . mconcat)
117− [ long "mode"
118− , short 'm'
119− , metavar "MODE"
120− , value Stdout
121− , help "Mode of operation: 'stdout' (default), 'inplace', or 'check'"
122− ]
123− <*> configParser
124− <*> (many . strArgument . mconcat)
125− [ metavar "FILE"
126− , help "Haskell source files to format or stdin (default)"
127− ]
140+optsParser =
141+ 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+ )
154+ <*> configParser
155+ <*> (many . strArgument . mconcat)
156+ [ metavar "FILE",
157+ help "Haskell source files to format or stdin (default)"
158+ ]
128159
129−configParser :: Parser Config
130−configParser = Config
131− <$> (fmap (fmap DynOption) . many . strOption . mconcat)
132− [ long "ghc-opt"
133− , short 'o'
134− , metavar "OPT"
135− , help "GHC options to enable (e.g. language extensions)"
136− ]
137− <*> (switch . mconcat)
138− [ long "unsafe"
139− , short 'u'
140− , help "Do formatting faster but without automatic detection of defects"
141− ]
142− <*> (switch . mconcat)
143− [ long "debug"
144− , short 'd'
145− , help "Output information useful for debugging"
146− ]
147− <*> (switch . mconcat)
148− [ long "tolerate-cpp"
149− , short 'p'
150− , help "Do not fail if CPP pragma is present"
151− ]
152− <*> (switch . mconcat)
153− [ long "check-idempotency"
154− , short 'c'
155− , help "Fail if formatting is not idempotent"
156− ]
160+configParser :: Parser (Config RegionIndices)
161+configParser =
162+ Config
163+ <$> (fmap (fmap DynOption) . many . strOption . mconcat)
164+ [ long "ghc-opt",
165+ short 'o',
166+ metavar "OPT",
167+ help "GHC options to enable (e.g. language extensions)"
168+ ]
169+ <*> (switch . mconcat)
170+ [ long "unsafe",
171+ short 'u',
172+ help "Do formatting faster but without automatic detection of defects"
173+ ]
174+ <*> (switch . mconcat)
175+ [ long "debug",
176+ short 'd',
177+ help "Output information useful for debugging"
178+ ]
179+ <*> (switch . mconcat)
180+ [ long "check-idempotence",
181+ short 'c',
182+ help "Fail if formatting is not idempotent"
183+ ]
184+ <*> ( RegionIndices
185+ <$> (optional . option auto . mconcat)
186+ [ long "start-line",
187+ metavar "START",
188+ help "Start line of the region to format (starts from 1)"
189+ ]
190+ <*> (optional . option auto . mconcat)
191+ [ long "end-line",
192+ metavar "END",
193+ help "End line of the region to format (inclusive)"
194+ ]
195+ )
157196
158197----------------------------------------------------------------------------
159198-- Helpers
160199
161200-- | Parse 'Mode'.
162−
163201parseMode :: ReadM Mode
164202parseMode = eitherReader $ \case
165203 "stdout" -> Right Stdout
data/examples/declaration/annotation/annotation-out.hs+1
@@ -18,4 +18,5 @@ foo = 5
1818
1919data Foo = Foo Int
2020{-# ANN type Foo ("HLint: ignore") #-}
21+
2122{- Comment -}
data/examples/declaration/class/associated-data1-out.hs−1
@@ -4,7 +4,6 @@ class Foo a where data FooBar a
44
55-- | Something.
66class Bar a where
7−
87 -- | Bar bar
98 data BarBar a
109
data/examples/declaration/class/associated-data2-out.hs−1
@@ -4,7 +4,6 @@ module Main where
44
55-- | Something more.
66class Baz a where
7−
87 -- | Baz bar
98 data
109 BazBar
data/examples/declaration/class/associated-type-defaults-out.hs−2
@@ -4,7 +4,6 @@ class Foo a where type FooBar a = Int
44
55-- | Something.
66class Bar a where
7−
87 -- Define bar
98 type
109 BarBar a =
@@ -18,7 +17,6 @@ class Bar a where
1817 a
1918
2019class Baz a where
21−
2220 type
2321 BazBar
2422 a
data/examples/declaration/class/associated-types1-out.hs−1
@@ -4,7 +4,6 @@ class Foo a where type FooBar a
44
55-- | Something.
66class Bar a where
7−
87 -- | Bar bar
98 type BarBar a
109
data/examples/declaration/class/associated-types2-out.hs−1
@@ -4,7 +4,6 @@ module Main where
44
55-- | Something more.
66class Baz a where
7−
87 -- | Baz bar
98 type
109 BazBar
data/examples/declaration/class/default-implementations-comments-out.hs−1
@@ -2,7 +2,6 @@ module Main where
22
33-- | Baz
44class Baz a where
5−
65 foobar :: a -> a
76 foobar a =
87 barbaz (bazbar a)
data/examples/declaration/class/default-signatures-out.hs−2
@@ -4,13 +4,11 @@ module Main where
44
55-- | Something else.
66class Bar a where
7−
87 -- | Bar
98 bar ::
109 String ->
1110 String ->
1211 a
13−
1412 -- Pointless comment
1513 default bar ::
1614 ( Read a,
data/examples/declaration/class/default-signatures-simple-out.hs−2
@@ -2,9 +2,7 @@ module Main where
22
33-- | Something.
44class Foo a where
5−
65 -- | Foo
76 foo :: a -> String
8−
97 default foo :: Show a => a -> String
108 foo = show
data/examples/declaration/class/dependency-super-classes-out.hs+2−4
@@ -4,9 +4,7 @@ module Main where
44
55-- | Something.
66class (MonadReader r s, MonadWriter w m) => MonadState s m | m -> s where
7−
87 get :: m s
9−
108 put :: s -> m ()
119
1210-- | 'MonadParsec'
@@ -15,8 +13,8 @@ class
1513 MonadPlus m -- Potential for failure
1614 ) =>
1715 MonadParsec e s m
18− | m -> e s where
19−
16+ | m -> e s
17+ where
2018 -- | 'getState' returns state
2119 getState ::
2220 m s
data/examples/declaration/class/functional-dependencies-out.hs+2−1
@@ -14,5 +14,6 @@ class
1414 b c -> a d, -- Bar
1515 a c -> b d, -- Baz
1616 a c d -> b,
17− a b d -> a b c d where
17+ a b d -> a b c d
18+ where
1819 baz :: a -> b
data/examples/declaration/class/multi-parameters2-out.hs+2−3
@@ -10,11 +10,10 @@ class
1010 c -- Baz bar
1111 d -- Baz baz
1212 e -- Rest
13− f where
14−
13+ f
14+ where
1515 barbaz ::
1616 a -> f
17−
1817 bazbar ::
1918 e ->
2019 f
data/examples/declaration/class/newlines-after-where-out.hsnew file+5
@@ -0,0 +1,5 @@
1+class Num a where
2+ (+) :: a -> a -> a
3+
4+class Num a where
5+ (+) :: a -> a -> a
data/examples/declaration/class/newlines-after-where.hsnew file+7
@@ -0,0 +1,7 @@
1+class Num a where
2+ (+) :: a -> a -> a
3+
4+class Num a where
5+
6+
7+ (+) :: a -> a -> a
data/examples/declaration/class/newlines-and-default-decls-out.hsnew file+11
@@ -0,0 +1,11 @@
1+class Foo a where
2+ foo :: a
3+ default foo :: ()
4+ foo = ()
5+ bar :: a
6+ default bar :: ()
7+ bar = ()
8+
9+ qux :: a
10+ default qux :: ()
11+ qux = ()
data/examples/declaration/class/newlines-and-default-decls.hsnew file+13
@@ -0,0 +1,13 @@
1+class Foo a where
2+ foo :: a
3+ default foo :: ()
4+ foo = ()
5+ bar :: a
6+ default bar :: ()
7+ bar = ()
8+
9+ qux :: a
10+
11+ default qux :: ()
12+
13+ qux = ()
data/examples/declaration/class/newlines-and-haddocks-out.hsnew file+23
@@ -0,0 +1,23 @@
1+class Foo a where
2+ -- | Haddock
3+ foo :: a
4+
5+ -- | Another Haddock
6+ bar :: a
7+
8+ baz :: a
9+ -- ^ Post-Haddock
10+
11+ raz :: a
12+ -- ^ Another Post-Haddock
13+
14+ -- | One more Haddock
15+ qux :: a
16+
17+ -- Comment before a Haddock
18+
19+ -- | And one more Haddock
20+ xyz :: a
21+
22+ -- | Haddock followed by a blank line
23+ abc :: a
data/examples/declaration/class/newlines-and-haddocks.hsnew file+19
@@ -0,0 +1,19 @@
1+class Foo a where
2+ -- | Haddock
3+ foo :: a
4+ -- | Another Haddock
5+ bar :: a
6+ baz :: a
7+ -- ^ Post-Haddock
8+ raz :: a
9+ -- ^ Another Post-Haddock
10+
11+ -- | One more Haddock
12+ qux :: a
13+ -- Comment before a Haddock
14+ -- | And one more Haddock
15+ xyz :: a
16+
17+ -- | Haddock followed by a blank line
18+
19+ abc :: a
data/examples/declaration/class/newlines-between-methods-out.hsnew file+17
@@ -0,0 +1,17 @@
1+class Num a where
2+ (+) :: a -> a -> a
3+ (-) :: a -> a -> a
4+ (*) :: a -> a -> a
5+
6+ -- Comment before definition
7+ negate :: a -> a
8+
9+ -- Comment after definition
10+
11+ -- Separator
12+
13+ abs :: a -> a
14+ signum :: a -> a
15+
16+ -- Comment between unrelated definitions
17+ fromInteger :: Integer -> a
data/examples/declaration/class/newlines-between-methods.hsnew file+15
@@ -0,0 +1,15 @@
1+class Num a where
2+ (+) :: a -> a -> a
3+ (-) :: a -> a -> a
4+ (*) :: a -> a -> a
5+
6+ -- Comment before definition
7+ negate :: a -> a
8+ -- Comment after definition
9+
10+ -- Separator
11+
12+ abs :: a -> a
13+ signum :: a -> a
14+ -- Comment between unrelated definitions
15+ fromInteger :: Integer -> a
data/examples/declaration/class/single-parameters-out.hs−2
@@ -21,8 +21,6 @@ class Baz a where
2121 a
2222
2323class BarBaz a where
24−
2524 barbaz ::
2625 a -> b
27−
2826 bazbar :: b -> a
data/examples/declaration/class/type-operators3-out.hs+1−1
@@ -1,7 +1,7 @@
1−{-# LANGUAGE NoStarIsType #-}
21{-# LANGUAGE PolyKinds #-}
32{-# LANGUAGE TypeFamilies #-}
43{-# LANGUAGE TypeOperators #-}
4+{-# LANGUAGE NoStarIsType #-}
55
66class PNum x where
77 type (a :: x) * (b :: x)
data/examples/declaration/data/existential-multiline-out.hs+12
@@ -3,3 +3,15 @@
33data Foo
44 = forall a. MkFoo a (a -> Bool)
55 | forall a. Eq a => MkBar a
6+
7+data Bar
8+ = forall x y.
9+ Bar x y x y
10+
11+data Baz
12+ = forall x y.
13+ Baz
14+ x
15+ y
16+ x
17+ y
data/examples/declaration/data/existential-multiline.hs+7
@@ -3,3 +3,10 @@
33data Foo
44 = forall a. MkFoo a (a -> Bool)
55 | forall a. Eq a => MkBar a
6+
7+data Bar = forall x y.
8+ Bar x y x y
9+
10+data Baz = forall x y.
11+ Baz x y x
12+ y
data/examples/declaration/data/field-layout/gadt-out.hs+2−1
@@ -6,5 +6,6 @@ data Foo a b where
66 Foo :: Foo Int Int
77 -- | Something else
88 Bar ::
9− Foo Char
9+ Foo
10+ Char
1011 Char
data/examples/declaration/data/field-layout/record-out.hs+9−9
@@ -1,12 +1,12 @@
11module Main where
22
33-- | Foo.
4−data Foo
5− = Foo
6− { -- | Something
7− foo :: Foo Int Int,
8− -- | Something else
9− bar ::
10− Bar Char
11− Char
12− }
4+data Foo = Foo
5+ { -- | Something
6+ foo :: Foo Int Int,
7+ -- | Something else
8+ bar ::
9+ Bar
10+ Char
11+ Char
12+ }
data/examples/declaration/data/gadt/multiline-out.hs+1−1
@@ -8,7 +8,7 @@ data Foo a where
88 Foo ::
99 forall a b.
1010 (Show a, Eq b) => -- foo
11− -- bar
11+ -- bar
1212 a ->
1313 b ->
1414 Foo 'Int
data/examples/declaration/data/gadt/multiline-where-out.hsnew file+7
@@ -0,0 +1,7 @@
1+data
2+ Foo
3+ a
4+ b
5+ c
6+ where
7+ Foo :: a -> b -> c -> Foo a b c
data/examples/declaration/data/gadt/multiline-where.hsnew file+6
@@ -0,0 +1,6 @@
1+data
2+ Foo
3+ a
4+ b
5+ c where
6+ Foo :: a -> b -> c -> Foo a b c
data/examples/declaration/data/multiline-arg-parens-out.hs+1−1
@@ -5,6 +5,6 @@ data Foo
55 = Foo
66 Bar
77 (Set Baz) -- and here we go
8− -- and that's it
8+ -- and that's it
99 Text
1010 deriving (Eq)
data/examples/declaration/data/partly-documented-out.hsnew file+5
@@ -0,0 +1,5 @@
1+data Optimisation
2+ = PETransform
3+ | -- | partial eval and associated transforms
4+ GeneralisedNatHack
5+ deriving (Show, Eq, Generic)
data/examples/declaration/data/partly-documented.hsnew file+2
@@ -0,0 +1,2 @@
1+data Optimisation = PETransform | GeneralisedNatHack -- ^ partial eval and associated transforms
2+ deriving (Show, Eq, Generic)
data/examples/declaration/data/record-multi-const-out.hsnew file+17
@@ -0,0 +1,17 @@
1+module Main where
2+
3+-- | Something.
4+data Foo
5+ = Foo
6+ { -- | X
7+ fooX :: Int,
8+ -- | Y
9+ fooY :: Int
10+ }
11+ | Bar
12+ { -- | X
13+ barX :: Int,
14+ -- | Y
15+ barY :: Int
16+ }
17+ deriving (Eq, Show)
data/examples/declaration/data/record-multi-const.hsnew file+13
@@ -0,0 +1,13 @@
1+module Main where
2+
3+-- | Something.
4+
5+data Foo = Foo
6+ { fooX :: Int -- ^ X
7+ , fooY :: Int -- ^ Y
8+ } |
9+ Bar
10+ { barX :: Int -- ^ X
11+ , barY :: Int -- ^ Y
12+ }
13+ deriving (Eq, Show)
data/examples/declaration/data/record-out.hs+19−20
@@ -1,24 +1,23 @@
11module Main where
22
33-- | Something.
4−data Foo
5− = Foo
6− { -- | X
7− fooX :: Int,
8− -- | Y
9− fooY :: Int,
10− -- | BarBaz
11− fooBar, fooBaz :: NonEmpty (Identity Bool),
12− -- | GagGog
13− fooGag,
14− fooGog ::
15− NonEmpty
16− ( Indentity
17− Bool
18− ),
19− -- | Huh!
20− fooFoo,
21− barBar ::
22− Int
23− }
4+data Foo = Foo
5+ { -- | X
6+ fooX :: Int,
7+ -- | Y
8+ fooY :: Int,
9+ -- | BarBaz
10+ fooBar, fooBaz :: NonEmpty (Identity Bool),
11+ -- | GagGog
12+ fooGag,
13+ fooGog ::
14+ NonEmpty
15+ ( Indentity
16+ Bool
17+ ),
18+ -- | Huh!
19+ fooFoo,
20+ barBar ::
21+ Int
22+ }
2423 deriving (Eq, Show)
data/examples/declaration/data/simple-broken-out.hs+1−2
@@ -1,8 +1,7 @@
11module Main where
22
33-- | Here we go.
4−data Foo
5− = Foo {unFoo :: Int}
4+data Foo = Foo {unFoo :: Int}
65 deriving (Eq)
76
87-- | And once again.
data/examples/declaration/data/unnamed-field-comment-0-out.hsnew file+7
@@ -0,0 +1,7 @@
1+data Foo
2+ = -- | Bar
3+ Bar
4+ Field1
5+ -- ^ Field 1
6+ Field2
7+ -- ^ Field 2
data/examples/declaration/data/unnamed-field-comment-0.hsnew file+5
@@ -0,0 +1,5 @@
1+data Foo
2+ = -- | Bar
3+ Bar
4+ Field1 -- ^ Field 1
5+ Field2 -- ^ Field 2
data/examples/declaration/data/unnamed-field-comment-1-out.hsnew file+5
@@ -0,0 +1,5 @@
1+data X
2+ = B
3+ !Int
4+ -- ^ y
5+ C
data/examples/declaration/data/unnamed-field-comment-1.hsnew file+4
@@ -0,0 +1,4 @@
1+data X
2+ = B
3+ !Int -- ^ y
4+ C
data/examples/declaration/data/with-comment-out.hsnew file+4
@@ -0,0 +1,4 @@
1+data A
2+ = B -- C
3+ | -- D
4+ E
data/examples/declaration/data/with-comment.hsnew file+5
@@ -0,0 +1,5 @@
1+data A =
2+ B -- C
3+
4+ -- D
5+ | E
data/examples/declaration/data/with-weird-haddock-out.hsnew file+4
@@ -0,0 +1,4 @@
1+data PlusLevel' t
2+ = -- | @n + ℓ@.
3+ Plus Integer (LevelAtom' t)
4+ deriving (Show, Data)
data/examples/declaration/data/with-weird-haddock.hsnew file+2
@@ -0,0 +1,2 @@
1+data PlusLevel' t = Plus Integer (LevelAtom' t) -- ^ @n + ℓ@.
2+ deriving (Show, Data)
data/examples/declaration/instance/associated-data-out.hs−2
@@ -11,9 +11,7 @@ instance Foo Double where
1111 Double
1212
1313instance Foo [a] where
14−
1514 data Bar [a]
1615 = ListBar [Bar a]
17−
1816 data Baz [a]
1917 = ListBaz
data/examples/declaration/instance/associated-data.hs+1
@@ -13,6 +13,7 @@ instance Foo Double where
1313
1414instance Foo [a]
1515 where
16+
1617 data Bar [a] =
1718 ListBar [Bar a]
1819 data Baz [a] =
data/examples/declaration/instance/associated-types-out.hs−1
@@ -3,7 +3,6 @@
33instance Foo Int where type Bar Int = Double
44
55instance Foo Double where
6−
76 type
87 Bar
98 Double =
data/examples/declaration/instance/associated-types.hs+2
@@ -9,5 +9,7 @@ instance Foo Double
99 Double
1010 =
1111 [Double]
12+
13+
1214 type instance Baz Double
1315 = [Double]
data/examples/declaration/instance/data-family-instances-gadt-out.hs+4−2
@@ -3,9 +3,11 @@
33
44data instance Bar Int a where
55 SameBar ::
6− Bar Int
6+ Bar
7+ Int
78 Int
89 CloseBar :: Bar Int Double
910 OtherBar ::
10− Bar Int
11+ Bar
12+ Int
1113 a
data/examples/declaration/instance/data-family-instances-newtype-out.hs+3−4
@@ -1,7 +1,6 @@
11{-# LANGUAGE GADTSyntax #-}
22{-# LANGUAGE TypeFamilies #-}
33
4−newtype instance Foo [Double]
5− = DoubleListFoo
6− { unDoubleListFoo :: Double
7− }
4+newtype instance Foo [Double] = DoubleListFoo
5+ { unDoubleListFoo :: Double
6+ }
data/examples/declaration/instance/instance-sigs-multiple-out.hs−2
@@ -1,12 +1,10 @@
11{-# LANGUAGE InstanceSigs #-}
22
33instance Applicative [] where
4−
54 pure ::
65 a ->
76 [a]
87 pure a = [a]
9−
108 (<*>) ::
119 [a] -> [a] -> [a]
1210 (<*>) _ _ = []
data/examples/declaration/instance/multi-parameter-out.hs−2
@@ -1,7 +1,5 @@
11instance MonadReader a ((->) a) where ask = id
22
33instance MonadState s (State s) where
4−
54 get = State.get
6−
75 put = State.put
data/examples/declaration/instance/newlines-after-where-out.hsnew file+5
@@ -0,0 +1,5 @@
1+instance Num X where
2+ (+) = undefined
3+
4+instance Num Y where
5+ (+) = undefined
data/examples/declaration/instance/newlines-after-where.hsnew file+7
@@ -0,0 +1,7 @@
1+instance Num X where
2+ (+) = undefined
3+
4+instance Num Y where
5+
6+
7+ (+) = undefined
data/examples/declaration/instance/newlines-between-methods-out.hsnew file+18
@@ -0,0 +1,18 @@
1+instance Num a => Num (Diff a) where
2+ D u dudx + D v dvdx = D (u + v) (dudx + dvdx)
3+ D u dudx - D v dvdx = D (u - v) (dudx - dvdx)
4+ D u dudx * D v dvdx = D (u * v) (u * dvdx + v * dudx)
5+
6+ -- Comment before definition
7+ negate (D u dudx) = D (- u) (- dudx)
8+ negate (Z u dudx) = undefined
9+
10+ -- Comment after definition
11+
12+ -- Separator
13+
14+ abs (D u _) = D (abs u) (signum u)
15+ signum (D u _) = D (signum u) 0
16+
17+ -- Comment between unrelated definitions
18+ fromInteger n = D (fromInteger n) 0
data/examples/declaration/instance/newlines-between-methods.hsnew file+16
@@ -0,0 +1,16 @@
1+instance Num a => Num (Diff a) where
2+ D u dudx + D v dvdx = D (u + v) (dudx + dvdx)
3+ D u dudx - D v dvdx = D (u - v) (dudx - dvdx)
4+ D u dudx * D v dvdx = D (u * v) (u * dvdx + v * dudx)
5+
6+ -- Comment before definition
7+ negate (D u dudx) = D (-u) (-dudx)
8+ negate (Z u dudx) = undefined
9+ -- Comment after definition
10+
11+ -- Separator
12+
13+ abs (D u _) = D (abs u) (signum u)
14+ signum (D u _) = D (signum u) 0
15+ -- Comment between unrelated definitions
16+ fromInteger n = D (fromInteger n) 0
data/examples/declaration/instance/single-parameter-out.hs−2
@@ -1,9 +1,7 @@
11instance Monoid Int where (<>) x y = x + y
22
33instance Enum Int where
4−
54 fromEnum x = x
6−
75 toEnum =
86 \x ->
97 x
data/examples/declaration/signature/minimal/minimal-out.hs−2
@@ -1,5 +1,4 @@
11class Foo a where
2−
32 {-# MINIMAL (==) | ((/=), foo) #-}
43
54 {-# MINIMAL
@@ -12,5 +11,4 @@ class Foo a where
1211 #-}
1312
1413 (==) :: a -> a -> Bool
15−
1614 (/=) :: a -> a -> Bool
data/examples/declaration/signature/specialize/specialize-instance-out.hs−4
@@ -3,16 +3,12 @@ data Foo a = Foo a
33data VT v m r = VT v m r
44
55instance (Eq a) => Eq (Foo a) where
6−
76 {-# SPECIALIZE instance Eq (Foo [(Int, Bar)]) #-}
8−
97 (==) (Foo a) (Foo b) = (==) a b
108
119instance (Num r, V.Vector v r, Factored m r) => Num (VT v m r) where
12−
1310 {-# SPECIALIZE instance
1411 ( Factored m Int => Num (VT U.Vector m Int)
1512 )
1613 #-}
17−
1814 VT x + VT y = VT $ V.zipWith (+) x y
data/examples/declaration/signature/standalone-kind-out.hsnew file+13
@@ -0,0 +1,13 @@
1+{-# LANGUAGE StandaloneKindSignatures #-}
2+
3+type T ::
4+ (k -> Type) ->
5+ k ->
6+ Type
7+data T m a = MkT (m a) (T Maybe (m a))
8+
9+type C1 :: Type -> Constraint
10+class C1 a
11+
12+type F :: Type -> Type
13+type family F
data/examples/declaration/signature/standalone-kind.hsnew file+13
@@ -0,0 +1,13 @@
1+{-# LANGUAGE StandaloneKindSignatures #-}
2+
3+type T :: (k -> Type)
4+ -> k
5+ -> Type
6+
7+data T m a = MkT (m a) (T Maybe (m a))
8+
9+type C1 :: Type -> Constraint
10+class C1 a
11+
12+type F :: Type -> Type
13+type family F
data/examples/declaration/signature/type/infix-promoted-type-constructor-out.hs−1
@@ -1,3 +1,2 @@
11fun1 :: Def ('[Ref s (Stored Uint32), IBool] 'T.:-> IBool)
2−
32fun2 :: Def ('[Ref s (Stored Uint32), IBool] ':-> IBool)
data/examples/declaration/signature/type/multi-value-out.hs+5
@@ -2,6 +2,11 @@ foo, bar :: Int
22foo = 1
33bar = 2
44
5+a, b, c :: Int
6+a = 1
7+b = 2
8+c = 3
9+
510foo,
611 bar,
712 baz ::
data/examples/declaration/signature/type/multi-value.hs+5
@@ -2,6 +2,11 @@ foo, bar :: Int
22foo = 1
33bar = 2
44
5+a, b, c :: Int
6+a = 1
7+b = 2
8+c = 3
9+
510foo,
611 bar,
712 baz :: Int
data/examples/declaration/signature/type/unrelated-out.hsnew file+4
@@ -0,0 +1,4 @@
1+clientFunc1 :: SomeType1
2+clientFunc2 :: SomeType2
3+clientFunc3 :: SomeType3
4+clientFunc1 :<|> clientFunc2 :<|> clientFunc3 = hoistClient foo bar baz
data/examples/declaration/signature/type/unrelated.hsnew file+4
@@ -0,0 +1,4 @@
1+clientFunc1 :: SomeType1
2+clientFunc2 :: SomeType2
3+clientFunc3 :: SomeType3
4+clientFunc1 :<|> clientFunc2 :<|> clientFunc3 = hoistClient foo bar baz
data/examples/declaration/splice/grouped-splices-out.hsnew file+3
@@ -0,0 +1,3 @@
1+$(deriveJSON fieldLabelMod ''A)
2+$(deriveJSON fieldLabelMod ''B)
3+$(deriveJSON fieldLabelMod ''C)
data/examples/declaration/splice/grouped-splices.hsnew file+3
@@ -0,0 +1,3 @@
1+$(deriveJSON fieldLabelMod ''A)
2+$(deriveJSON fieldLabelMod ''B)
3+$(deriveJSON fieldLabelMod ''C)
data/examples/declaration/splice/splice-decl.hs−1
@@ -15,4 +15,3 @@ foo bar
1515-- TemplateHaskell allows Q () at the top level
1616do
1717 pure []
18−
data/examples/declaration/type-families/closed-type-family/multi-line-out.hs+6−5
@@ -1,7 +1,7 @@
11type family
22 Id a =
3− result
4− | result -> a where
3+ result | result -> a
4+ where
55 Id a =
66 a
77
@@ -10,14 +10,15 @@ type family
1010 (a :: k)
1111 b
1212 c =
13− foo
14− | foo -> k b where
13+ foo | foo -> k b
14+ where
1515 G a b c =
1616 (a, b)
1717
1818type family
1919 F a ::
20− * -> * where
20+ * -> *
21+ where
2122 F Int = Double
2223 F Bool =
2324 Char
data/examples/declaration/type-synonyms/with-weird-haddock-out.hsnew file+3
@@ -0,0 +1,3 @@
1+type Elims =
2+ -- | eliminations ordered left-to-right.
3+ [Elim]
data/examples/declaration/type-synonyms/with-weird-haddock.hsnew file+1
@@ -0,0 +1 @@
1+type Elims = [Elim] -- ^ eliminations ordered left-to-right.
data/examples/declaration/type/visible-forall-out.hsnew file+2
@@ -0,0 +1,2 @@
1+-- source: https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0081-forall-arrow.rst
2+data T :: forall k -> k -> Type
data/examples/declaration/type/visible-forall.hsnew file+2
@@ -0,0 +1,2 @@
1+-- source: https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0081-forall-arrow.rst
2+data T :: forall k -> k -> Type
data/examples/declaration/value/function/application-out.hs → data/examples/declaration/value/function/application-0-out.hsrenamed

The content is the same.

data/examples/declaration/value/function/application.hs → data/examples/declaration/value/function/application-0.hsrenamed

The content is the same.

data/examples/declaration/value/function/application-1-out.hsnew file+19
@@ -0,0 +1,19 @@
1+main =
2+ do
3+ x
4+ y
5+ z
6+
7+main =
8+ case foo of
9+ x -> a
10+ foo
11+ a
12+ b
13+
14+main =
15+ do
16+ if x then y else z
17+ foo
18+ a
19+ b
data/examples/declaration/value/function/application-1.hsnew file+14
@@ -0,0 +1,14 @@
1+main =
2+ do
3+ x
4+ y
5+ z
6+
7+main =
8+ case foo of
9+ x -> a
10+ foo a b
11+
12+main = do
13+ if x then y else z
14+ foo a b
data/examples/declaration/value/function/arg-breakpoints-out.hsnew file+3
@@ -0,0 +1,3 @@
1+foo
2+ bar =
3+ body
data/examples/declaration/value/function/arg-breakpoints.hsnew file+3
@@ -0,0 +1,3 @@
1+foo
2+ bar =
3+ body
data/examples/declaration/value/function/arrow/expression-applications-out.hsdeleted−9
@@ -1,9 +0,0 @@
1−{-# LANGUAGE Arrows #-}
2−
3−foo x y = x -< y
4−
5−bar f x =
6− f x -< -- Hello
7− x -- World
8−
9−baz x y = x -<< y
data/examples/declaration/value/function/arrow/expression-applications.hsdeleted−9
@@ -1,9 +0,0 @@
1−{-# LANGUAGE Arrows #-}
2−
3−foo x y = x -< y
4−
5−bar f x
6− = f x -- Hello
7− -< x -- World
8−
9−baz x y = x -<< y
data/examples/declaration/value/function/arrow/expression-forms-out.hsdeleted−14
@@ -1,14 +0,0 @@
1−{-# LANGUAGE Arrows #-}
2−
3−foo f g x y = (| test (f -< x) (g -< y) |)
4−
5−bar f g x y =
6− (|
7− test
8− ( f -<
9− x
10− )
11− ( g -<
12− y
13− )
14− |)
data/examples/declaration/value/function/arrow/expression-forms.hsdeleted−11
@@ -1,11 +0,0 @@
1−{-# LANGUAGE Arrows #-}
2−
3−foo f g x y = (| test (f -< x) (g -< y) |)
4−
5−bar f g x y = (|
6− test
7− (f
8− -< x)
9− (g
10− -< y)
11− |)
data/examples/declaration/value/function/arrow/multiline-case-out.hsnew file+8
@@ -0,0 +1,8 @@
1+{-# LANGUAGE Arrows #-}
2+
3+f = proc x -> do
4+ x <-
5+ case x of X -> x
6+ -<
7+ y
8+ a -< b
data/examples/declaration/value/function/arrow/multiline-case.hsnew file+7
@@ -0,0 +1,7 @@
1+{-# LANGUAGE Arrows #-}
2+
3+f = proc x -> do
4+ x
5+ <- case x of X -> x
6+ -< y
7+ a -< b
data/examples/declaration/value/function/arrow/proc-applications-out.hs+6−5
@@ -9,10 +9,11 @@ bar f x =
99 w
1010 )
1111 ->
12− f -< -- The value
13− ( x, -- Foo
14− w, -- Bar
15− z -- Baz
16− )
12+ f -- The value
13+ -<
14+ ( x, -- Foo
15+ w, -- Bar
16+ z -- Baz
17+ )
1718
1819baz x = proc a -> a -<< x
data/examples/declaration/value/function/arrow/proc-do-complex-out.hs+18−14
@@ -19,10 +19,11 @@ foo
1919 )
2020 ( b,
2121 d
22− ) -<
23− ( b + 1, -- Funnel into arrow
24− d * b
2522 )
23+ -<
24+ ( b + 1, -- Funnel into arrow
25+ d * b
26+ )
2627 if x `mod` y == 0 -- Basic condition
2728 then case e of -- Only left case is relevant
2829 Left
@@ -32,18 +33,20 @@ foo
3233 let v =
3334 u -- Actually never used
3435 ^ 2
35− in ( returnA -<
36− -- Just do the calculation
37− (x + y * z)
36+ in ( returnA
37+ -<
38+ -- Just do the calculation
39+ (x + y * z)
3840 )
3941 else do
4042 let u = x -- Let bindings bind expressions, not commands
41− -- Could pattern match directly on x
43+ -- Could pattern match directly on x
4244 i <- case u of
4345 0 -> (g . h -< u)
4446 n ->
45− ( ( h . g -<
46− y -- First actual use of y
47+ ( ( h . g
48+ -<
49+ y -- First actual use of y
4750 )
4851 )
4952 returnA -< ()
@@ -51,8 +54,9 @@ foo
5154 if i > 0
5255 then ma -< ()
5356 else returnA -< ()
54− returnA -<
55− ( i
56− + x
57− * y -- Just do the calculation
58− )
57+ returnA
58+ -<
59+ ( i
60+ + x
61+ * y -- Just do the calculation
62+ )
data/examples/declaration/value/function/arrow/proc-do-simple1-out.hs+3−2
@@ -13,5 +13,6 @@ barbaz f g = proc (a, b) -> do
1313
1414bazbar f = proc a -> do
1515 a <-
16− f -<
17− a
16+ f
17+ -<
18+ a
data/examples/declaration/value/function/arrow/proc-do-simple2-out.hs+9−7
@@ -11,10 +11,12 @@ bazbaz f g h = proc (a, b, c) -> do
1111 z <-
1212 h
1313 x
14− y -<
15− ( a,
16− b,
17− c
18− )
19− returnA -<
20− (x, y, z)
14+ y
15+ -<
16+ ( a,
17+ b,
18+ c
19+ )
20+ returnA
21+ -<
22+ (x, y, z)
data/examples/declaration/value/function/arrow/proc-forms1-out.hs+8−6
@@ -7,13 +7,15 @@ foo1 f g h x =
77 (|
88 test
99 ( h f
10− . h g -<
11− y x
12− . y z
10+ . h g
11+ -<
12+ y x
13+ . y z
1314 )
1415 ( h g
15− . h f -<
16− y z
17− . y x
16+ . h f
17+ -<
18+ y z
19+ . y x
1820 )
1921 |)
data/examples/declaration/value/function/arrow/proc-forms2-out.hs+10−6
@@ -4,15 +4,19 @@ bar0 f g h x =
44 proc (y, z) ->
55 (| test (h f . (h g) -< (y x) . y z) ((h g) . h f -< y z . (y x)) |)
66
7−bar1 f g x y = proc _ -> f -< x &&& g -< y
7+bar1 f g x y = proc _ -> (f -< x) &&& (g -< y)
88
99bar2 f g h x =
1010 proc (y, z) ->
11− h f . (h g) -< (y x) . y z ||| (h g) . h f -< y z . (y x)
11+ (h f . (h g) -< (y x) . y z) ||| ((h g) . h f -< y z . (y x))
1212
1313bar3 f g h x =
1414 proc (y, z) ->
15− (h f . h g) -<
16− (y x) . y z
17− ||| (h g . h f) -<
18− y z . (y x)
15+ ( (h f . h g)
16+ -<
17+ (y x) . y z
18+ ) |||
19+ ( (h g . h f)
20+ -<
21+ y z . (y x)
22+ )
data/examples/declaration/value/function/arrow/proc-forms2.hs+6−6
@@ -4,16 +4,16 @@ bar0 f g h x =
44 proc (y, z) ->
55 (| test (h f.(h g) -< (y x).y z)((h g) . h f-<y z . (y x)) |)
66
7−bar1 f g x y = proc _ -> f -< x&&&g -< y
7+bar1 f g x y = proc _ -> (f -< x)&&&(g -< y)
88
99bar2 f g h x =
1010 proc (y, z) ->
11− h f.(h g) -< (y x).y z ||| (h g) . h f-<y z . (y x)
11+ (h f.(h g) -< (y x).y z) ||| ((h g) . h f-<y z . (y x))
1212
1313bar3 f g h x =
1414 proc (y, z) ->
15− (h f.h g)
16− -< (y x).y z
15+ ((h f.h g)
16+ -< (y x).y z)
1717 |||
18− (h g . h f)
19− -<y z . (y x)
18+ ((h g . h f)
19+ -<y z . (y x))
data/examples/declaration/value/function/arrow/proc-ifs-out.hs+6−4
@@ -6,8 +6,10 @@ bar f g = proc a ->
66 if f a
77 then
88 f
9− . g -<
10− a
9+ . g
10+ -<
11+ a
1112 else
12− g -<
13− b
13+ g
14+ -<
15+ b
data/examples/declaration/value/function/arrow/proc-parentheses-out.hs+5−4
@@ -6,13 +6,14 @@ bar f g = proc a ->
66 ( ( (f)
77 ( g
88 )
9− ) -<
10− ( ( ( ( ( ( g
11− a
9+ )
10+ -<
11+ ( ( ( ( ( ( g
12+ a
13+ )
1214 )
1315 )
1416 )
1517 )
1618 )
17− )
1819 )
data/examples/declaration/value/function/arrow/recursive-procs-out.hs+14−10
@@ -3,17 +3,21 @@
33foo f g = proc (x, y) -> do
44 rec a <- f y -< x
55 b <-
6− g x -<
7− y
8− bar -<
9− ( a,
10− b
11− )
6+ g x
7+ -<
8+ y
9+ bar
10+ -<
11+ ( a,
12+ b
13+ )
1214 rec p <-
1315 f
14− p -<
15− a
16+ p
17+ -<
18+ a
1619 rec q <-
1720 g
18− q -<
19− b
21+ q
22+ -<
23+ b
data/examples/declaration/value/function/awkward-comment-0-out.hsnew file+6
@@ -0,0 +1,6 @@
1+mergeErrorReply :: ParseError -> Reply s u a -> Reply s u a
2+mergeErrorReply err1 reply -- XXX where to put it?
3+ =
4+ case reply of
5+ Ok x state err2 -> Ok x state (mergeError err1 err2)
6+ Error err2 -> Error (mergeError err1 err2)
data/examples/declaration/value/function/awkward-comment-0.hsnew file+5
@@ -0,0 +1,5 @@
1+mergeErrorReply :: ParseError -> Reply s u a -> Reply s u a
2+mergeErrorReply err1 reply -- XXX where to put it?
3+ = case reply of
4+ Ok x state err2 -> Ok x state (mergeError err1 err2)
5+ Error err2 -> Error (mergeError err1 err2)
data/examples/declaration/value/function/awkward-comment-1-out.hsnew file+9
@@ -0,0 +1,9 @@
1+doForeign :: Vars -> [Name] -> [Term] -> Idris LExp
2+doForeign x = x
3+ where
4+ splitArg tm | (_, [_, _, l, r]) <- unApply tm -- pair, two implicits
5+ =
6+ do
7+ let l' = toFDesc l
8+ r' <- irTerm (sMN 0 "__foreignCall") vs env r
9+ return (l', r')
data/examples/declaration/value/function/awkward-comment-1.hsnew file+7
@@ -0,0 +1,7 @@
1+doForeign :: Vars -> [Name] -> [Term] -> Idris LExp
2+doForeign x = x
3+ where
4+ splitArg tm | (_, [_,_,l,r]) <- unApply tm -- pair, two implicits
5+ = do let l' = toFDesc l
6+ r' <- irTerm (sMN 0 "__foreignCall") vs env r
7+ return (l', r')
data/examples/declaration/value/function/blank-lines-let-out.hsnew file+6
@@ -0,0 +1,6 @@
1+foo =
2+ let x = 10
3+
4+ y = 11
5+ z = 12
6+ in x + y + z
data/examples/declaration/value/function/blank-lines-let.hsnew file+9
@@ -0,0 +1,9 @@
1+foo =
2+ let
3+
4+ x = 10
5+
6+ y = 11
7+ z = 12
8+
9+ in x + y + z
data/examples/declaration/value/function/blank-lines-where-out.hsnew file+6
@@ -0,0 +1,6 @@
1+foo = x + y + z
2+ where
3+ x = 10
4+
5+ y = 11
6+ z = 12
data/examples/declaration/value/function/blank-lines-where.hsnew file+7
@@ -0,0 +1,7 @@
1+foo = x + y + z
2+ where
3+
4+ x = 10
5+
6+ y = 11
7+ z = 12
data/examples/declaration/value/function/block-arguments-out.hs+5
@@ -27,3 +27,8 @@ f8 = foo \x ->
2727 y
2828
2929f9 = foo do { bar } baz
30+
31+f10 = foo
32+ do a
33+ do b
34+ do c
data/examples/declaration/value/function/block-arguments.hs+5
@@ -23,3 +23,8 @@ f8 = foo \x ->
2323 y
2424
2525f9 = foo do { bar } baz
26+
27+f10 = foo
28+ do { a }
29+ do { b }
30+ do c
data/examples/declaration/value/function/do/blocks-out.hs+1
@@ -15,4 +15,5 @@ readInClause = do
1515 <|> do
1616 optional g_Semi
1717 void allspacing
18+
1819 return things
data/examples/declaration/value/function/do/comment-alignment-out.hsnew file+9
@@ -0,0 +1,9 @@
1+main = do
2+ case blah of
3+ Nothing -> return ()
4+ Just xs -> do
5+ forM_ xs foo
6+ bar
7+ -- and here it goes
8+ unless bobla $
9+ quux
data/examples/declaration/value/function/do/comment-alignment.hsnew file+9
@@ -0,0 +1,9 @@
1+main = do
2+ case blah of
3+ Nothing -> return ()
4+ Just xs -> do
5+ forM_ xs foo
6+ bar
7+ -- and here it goes
8+ unless bobla $
9+ quux
data/examples/declaration/value/function/do/comment-spacing-out.hsnew file+7
@@ -0,0 +1,7 @@
1+foo = do
2+ bar
3+ -- foo
4+ baz
5+
6+ -- foo
7+ quux
data/examples/declaration/value/function/do/comment-spacing.hsnew file+7
@@ -0,0 +1,7 @@
1+foo = do
2+ bar
3+ -- foo
4+ baz
5+
6+ -- foo
7+ quux
data/examples/declaration/value/function/do/recursive-do-rec-out.hs+5
@@ -2,13 +2,18 @@
22
33foo = do
44 rec a <- b + 5
5+
56 let d = c
7+
68 b <- a * 5
9+
710 something
11+
812 c <- a + b
913 print c
1014 rec something $ do
1115 x <- a
1216 print x
17+
1318 y <- c
1419 print y
data/examples/declaration/value/function/if-with-comment-out.hsnew file+8
@@ -0,0 +1,8 @@
1+foo =
2+ if undefined
3+ then -- then comment
4+ undefined
5+ else -- else comment
6+
7+ do
8+ undefined
data/examples/declaration/value/function/if-with-comment.hsnew file+8
@@ -0,0 +1,8 @@
1+foo =
2+ if undefined
3+ -- then comment
4+ then undefined
5+ -- else comment
6+ else
7+ do
8+ undefined
data/examples/declaration/value/function/implicit-params-out.hs+1
@@ -1,6 +1,7 @@
11{-# LANGUAGE ImplicitParams #-}
22
33sortBy :: (a -> a -> Bool) -> [a] -> [a]
4+sortBy = undefined
45
56sort :: (?cmp :: a -> a -> Bool) => [a] -> [a]
67sort = sortBy ?cmp
data/examples/declaration/value/function/implicit-params.hs+2
@@ -1,6 +1,8 @@
11{-# LANGUAGE ImplicitParams #-}
22sortBy :: (a -> a -> Bool) -> [a] -> [a]
33
4+sortBy = undefined
5+
46sort :: (?cmp :: a -> a -> Bool) => [a] -> [a]
57sort = sortBy ?cmp
68
data/examples/declaration/value/function/infix/dollar-chains-out.hs+7−3
@@ -13,6 +13,10 @@ foo =
1313 throwIO (OrmoluCppEnabled path)
1414
1515foo =
16− bar
17− $ baz
18− $ quux
16+ bar $
17+ baz $
18+ quux
19+
20+x =
21+ case l of { A -> B } $
22+ case q of r -> s
data/examples/declaration/value/function/infix/dollar-chains.hs+4
@@ -16,3 +16,7 @@ foo =
1616 bar
1717 $ baz
1818 $ quux
19+
20+x =
21+ case l of { A -> B } $
22+ case q of r -> s
data/examples/declaration/value/function/infix/hanging-out.hs+4−3
@@ -17,6 +17,7 @@ update =
1717 `catch` \case
1818 a -> a
1919
20−foo = bar
21− ++ case foo of
22− a -> a
20+foo =
21+ bar
22+ ++ case foo of
23+ a -> a
data/examples/declaration/value/function/infix/lenses-out.hs+8−7
@@ -1,11 +1,12 @@
11lenses =
2− Just $ M.fromList $
3− "type" .= ("user.connection" :: Text)
4− # "connection" .= uc
5− # "user" .= case name of
6− Just n -> Just $ object ["name" .= n]
7− Nothing -> Nothing
8− # []
2+ Just $
3+ M.fromList $
4+ "type" .= ("user.connection" :: Text)
5+ # "connection" .= uc
6+ # "user" .= case name of
7+ Just n -> Just $ object ["name" .= n]
8+ Nothing -> Nothing
9+ # []
910
1011foo =
1112 a
data/examples/declaration/value/function/let-single-line-out.hs−1
@@ -5,7 +5,6 @@ foo x = let x = z where { z = 2 }; a = 3 in x
55foo x = let g :: Int -> Int; g = id in ()
66
77let a = b; c = do { foo; bar }; d = baz in b
8−
98let a = case True of { True -> foo; False -> bar }; b = foo a in b
109
1110foo x = let ?g = id; ?f = g in x
data/examples/declaration/value/function/list-notation-0-out.hsnew file+5
@@ -0,0 +1,5 @@
1+foo =
2+ testCase "Foo" testFoo :
3+ testCase "Bar" testBar :
4+ testCase "Baz" testBaz :
5+ []
data/examples/declaration/value/function/list-notation-0.hsnew file+5
@@ -0,0 +1,5 @@
1+foo =
2+ testCase "Foo" testFoo :
3+ testCase "Bar" testBar :
4+ testCase "Baz" testBaz :
5+ []
data/examples/declaration/value/function/list-notation-1-out.hsnew file+8
@@ -0,0 +1,8 @@
1+instance A.ToJSON UpdateTable where
2+ toJSON a =
3+ A.object $
4+ "TableName" .= updateTableName a :
5+ "ProvisionedThroughput" .= updateProvisionedThroughput a :
6+ case updateGlobalSecondaryIndexUpdates a of
7+ [] -> []
8+ l -> ["GlobalSecondaryIndexUpdates" .= l]
data/examples/declaration/value/function/list-notation-1.hsnew file+7
@@ -0,0 +1,7 @@
1+instance A.ToJSON UpdateTable where
2+ toJSON a = A.object
3+ $ "TableName" .= updateTableName a
4+ : "ProvisionedThroughput" .= updateProvisionedThroughput a
5+ : case updateGlobalSecondaryIndexUpdates a of
6+ [] -> []
7+ l -> [ "GlobalSecondaryIndexUpdates" .= l ]
data/examples/declaration/value/function/list-notation-2-out.hsnew file+6
@@ -0,0 +1,6 @@
1+-- A list of the element and all its parents up to the root node.
2+getPath tree t =
3+ t :
4+ case Map.lookup (getId t) tree of
5+ Nothing -> []
6+ Just parent -> getPath tree parent
data/examples/declaration/value/function/list-notation-2.hsnew file+5
@@ -0,0 +1,5 @@
1+-- A list of the element and all its parents up to the root node.
2+getPath tree t = t :
3+ case Map.lookup (getId t) tree of
4+ Nothing -> []
5+ Just parent -> getPath tree parent
data/examples/declaration/value/function/list-notation-3-out.hsnew file+12
@@ -0,0 +1,12 @@
1+foo =
2+ reportSDoc "tc.cc" 30 $
3+ sep $ do
4+ (prettyTCM q <+> " before compilation") : do
5+ map (prettyTCM . map unArg . clPats) cls
6+
7+foo =
8+ reportSDoc "tc.cc" 30 $
9+ sep $ do
10+ (prettyTCM q <+> " before compilation") :
11+ do
12+ map (prettyTCM . map unArg . clPats) cls
data/examples/declaration/value/function/list-notation-3.hsnew file+10
@@ -0,0 +1,10 @@
1+foo =
2+ reportSDoc "tc.cc" 30 $ sep $ do
3+ (prettyTCM q <+> " before compilation") : do
4+ map (prettyTCM . map unArg . clPats) cls
5+
6+foo =
7+ reportSDoc "tc.cc" 30 $ sep $ do
8+ (prettyTCM q <+> " before compilation") :
9+ do
10+ map (prettyTCM . map unArg . clPats) cls
data/examples/declaration/value/function/multi-way-if-out.hs+10−4
@@ -4,7 +4,13 @@ foo x = if | x == 5 -> 5
44
55bar x y =
66 if
7− | x > y -> x
8− | x < y ->
9− y
10− | otherwise -> x
7+ | x > y -> x
8+ | x < y ->
9+ y
10+ | otherwise -> x
11+
12+baz =
13+ if
14+ | p -> f
15+ | otherwise -> g
16+ x
data/examples/declaration/value/function/multi-way-if.hs+5
@@ -7,3 +7,8 @@ bar x y = if
77 | x < y
88 -> y
99 | otherwise -> x
10+
11+baz =
12+ if | p -> f
13+ | otherwise -> g
14+ x
data/examples/declaration/value/function/operator-comments-2-out.hsnew file+5
@@ -0,0 +1,5 @@
1+x =
2+ y
3+ ++ f g -- commentA
4+ -- commentB
5+ -- commentC
data/examples/declaration/value/function/operator-comments-2.hsnew file+4
@@ -0,0 +1,4 @@
1+x =
2+ y ++ -- commentA
3+-- commentB
4+ f g -- commentC
data/examples/declaration/value/function/operators-0-out.hsnew file+5
@@ -0,0 +1,5 @@
1+a =
2+ b & c .~ d
3+ & e
4+ %~ f
5+ g
data/examples/declaration/value/function/operators-0.hsnew file+4
@@ -0,0 +1,4 @@
1+a =
2+ b & c .~ d
3+ & e %~ f
4+ g
data/examples/declaration/value/function/operators-1-out.hsnew file+4
@@ -0,0 +1,4 @@
1+foo =
2+ f
3+ . g
4+ =<< h . i
data/examples/declaration/value/function/operators-1.hsnew file+3
@@ -0,0 +1,3 @@
1+foo = f
2+ . g
3+ =<< h . i
data/examples/declaration/value/function/operators-2-out.hsnew file+4
@@ -0,0 +1,4 @@
1+foo n
2+ | x || y && z || n ** x
3+ || x && n =
4+ 42
data/examples/declaration/value/function/operators-2.hsnew file+4
@@ -0,0 +1,4 @@
1+foo n
2+ | x || y && z || n ** x
3+ || x && n =
4+ 42
data/examples/declaration/value/function/operators-3-out.hsnew file+3
@@ -0,0 +1,3 @@
1+foo =
2+ op <> n <+> colon <+> prettySe <+> text "="
3+ <+> prettySe <> text sc
data/examples/declaration/value/function/operators-3.hsnew file+3
@@ -0,0 +1,3 @@
1+foo =
2+ op <> n <+> colon <+> prettySe <+> text "=" <+>
3+ prettySe <> text sc
data/examples/declaration/value/function/operators-4-out.hsnew file+3
@@ -0,0 +1,3 @@
1+foo =
2+ line <> bindingOf <+> text "=" <+> tPretty <+> colon
3+ <+> align <> prettyPs
data/examples/declaration/value/function/operators-4.hsnew file+3
@@ -0,0 +1,3 @@
1+foo =
2+ line <> bindingOf <+> text "=" <+> tPretty <+> colon <+>
3+ align <> prettyPs
data/examples/declaration/value/function/operators-5-out.hsnew file+5
@@ -0,0 +1,5 @@
1+foo =
2+ map bar $
3+ [ baz
4+ ]
5+ ++ quux
data/examples/declaration/value/function/operators-5.hsnew file+4
@@ -0,0 +1,4 @@
1+foo =
2+ map bar $
3+ [ baz
4+ ] ++ quux
data/examples/declaration/value/function/operators-6-out.hsnew file+9
@@ -0,0 +1,9 @@
1+type PermuteRef =
2+ "a"
3+ :> ( "b" :> "c" :> End
4+ :<|> "c" :> "b" :> End
5+ )
6+ :<|> "b"
7+ :> ( "a" :> "c" :> End
8+ :<|> "c" :> "a" :> End
9+ )
data/examples/declaration/value/function/operators-6.hsnew file+7
@@ -0,0 +1,7 @@
1+type PermuteRef =
2+ "a" :> ( "b" :> "c" :> End
3+ :<|> "c" :> "b" :> End
4+ )
5+ :<|> "b" :> ( "a" :> "c" :> End
6+ :<|> "c" :> "a" :> End
7+ )
data/examples/declaration/value/function/overindentation-out.hs+11−9
@@ -1,9 +1,11 @@
1−reallyincrediblyLongName = f
2− a
3− A
4− { reallyincrediblyLongName = f
5− a
6− A
7− { reallyincrediblyLongName
8− }
9− }
1+reallyincrediblyLongName =
2+ f
3+ a
4+ A
5+ { reallyincrediblyLongName =
6+ f
7+ a
8+ A
9+ { reallyincrediblyLongName
10+ }
11+ }
data/examples/declaration/value/function/pattern/famous-cardano-pattern-out.hs+8−8
@@ -1,8 +1,8 @@
1−( getNodeSettingsR :<|>
2− getNodeInfoR :<|>
3− getNextUpdateR :<|>
4− restartNodeR
5− ) :<|>
6− ( getUtxoR :<|>
7− getConfirmedProposalsR
8− ) = client nodeV1Api
1+( getNodeSettingsR
2+ :<|> getNodeInfoR
3+ :<|> getNextUpdateR
4+ :<|> restartNodeR
5+ )
6+ :<|> ( getUtxoR
7+ :<|> getConfirmedProposalsR
8+ ) = client nodeV1Api
data/examples/declaration/value/function/pattern/multiline-case-pattern-out.hs+2−2
@@ -10,6 +10,6 @@ f xs = case xs of
1010 ] -> a + b
1111
1212g xs = case xs of
13− ( a :
14− bs
13+ ( a
14+ : bs
1515 ) -> a + b
data/examples/declaration/value/function/pattern/view-pattern-out.hs−3
@@ -13,6 +13,3 @@ multiline
1313 bar
1414 baz
1515 ) = True
16−
17−-- https://github.com/tweag/ormolu/issues/343
18−foo = (f -> 4)
data/examples/declaration/value/function/pattern/view-pattern.hs−3
@@ -10,6 +10,3 @@ g ((f, _), f -> 4) = True
1010multiline (t -> Foo
1111 bar
1212 baz) = True
13−
14−-- https://github.com/tweag/ormolu/issues/343
15−foo = (f -> 4)
data/examples/declaration/value/function/record-constructors-out.hsdeleted−27
@@ -1,27 +0,0 @@
1−foo = Foo {a = 3}
2−
3−bar = Bar
4− { abc = foo,
5− def = Foo {a = 10}
6− }
7−
8−baz = Baz {}
9−
10−sym = Foo {(+) = 3}
11−
12−aLongVariableName =
13− ALongRecordName
14− { short = baz,
15− aLongRecordFieldName = YetAnotherLongRecordName
16− { yetAnotherLongRecordFieldName = "a long string"
17− },
18− aLongRecordFieldName2 = Just YetAnotherLongRecordName
19− { yetAnotherLongRecordFieldName = "a long string",
20− yetAnotherLongRecordFieldName =
21− Just
22− "a long string"
23− },
24− aLongRecordFieldName3 = do
25− foo
26− bar
27− }
data/examples/declaration/value/function/record/constructors-out.hsnew file+31
@@ -0,0 +1,31 @@
1+foo = Foo {a = 3}
2+
3+bar =
4+ Bar
5+ { abc = foo,
6+ def = Foo {a = 10}
7+ }
8+
9+baz = Baz {}
10+
11+sym = Foo {(+) = 3}
12+
13+aLongVariableName =
14+ ALongRecordName
15+ { short = baz,
16+ aLongRecordFieldName =
17+ YetAnotherLongRecordName
18+ { yetAnotherLongRecordFieldName = "a long string"
19+ },
20+ aLongRecordFieldName2 =
21+ Just
22+ YetAnotherLongRecordName
23+ { yetAnotherLongRecordFieldName = "a long string",
24+ yetAnotherLongRecordFieldName =
25+ Just
26+ "a long string"
27+ },
28+ aLongRecordFieldName3 = do
29+ foo
30+ bar
31+ }
data/examples/declaration/value/function/record-constructors.hs → data/examples/declaration/value/function/record/constructors.hsrenamed

The content is the same.

data/examples/declaration/value/function/record/dot-out.hsnew file+15
@@ -0,0 +1,15 @@
1+{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}
2+
3+data Foo = Foo {bar :: Int}
4+
5+mfoo = fmap (.bar) $ Nothing
6+
7+bar = (Foo 1).bar
8+
9+fooplus f n = f{foo = f.bar + n}
10+
11+fooplus' f n = f {foo = f.bar + n}
12+
13+fooplus'' f n = f {foo = f.bar + n}
14+
15+fooplus''' f n = f {foo = f.bar + n}
data/examples/declaration/value/function/record/dot.hsnew file+14
@@ -0,0 +1,14 @@
1+{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}
2+data Foo = Foo { bar :: Int }
3+
4+mfoo = fmap (.bar) $ Nothing
5+
6+bar = ( Foo 1).bar
7+
8+fooplus f n = f{foo = f.bar + n}
9+
10+fooplus' f n = f { foo = f.bar + n}
11+
12+fooplus'' f n = f {foo = f.bar + n}
13+
14+fooplus''' f n = f{ foo = f.bar + n}
data/examples/declaration/value/function/record/hanging-after-equal-con-out.hsnew file+8
@@ -0,0 +1,8 @@
1+foo =
2+ Foo
3+ { foo =
4+ -- Blah blah
5+ case x of
6+ Blah -> 1
7+ Blim -> 2
8+ }
data/examples/declaration/value/function/record/hanging-after-equal-con.hsnew file+7
@@ -0,0 +1,7 @@
1+foo = Foo
2+ { foo =
3+ -- Blah blah
4+ case x of
5+ Blah -> 1
6+ Blim -> 2
7+ }
data/examples/declaration/value/function/record/hanging-after-equal-update-out.hsnew file+8
@@ -0,0 +1,8 @@
1+foo =
2+ foo
3+ { foo =
4+ -- Blah blah
5+ case x of
6+ Blah -> 1
7+ Blim -> 2
8+ }
data/examples/declaration/value/function/record/hanging-after-equal-update.hsnew file+7
@@ -0,0 +1,7 @@
1+foo = foo
2+ { foo =
3+ -- Blah blah
4+ case x of
5+ Blah -> 1
6+ Blim -> 2
7+ }
data/examples/declaration/value/function/record/inter-comments-out.hsnew file+13
@@ -0,0 +1,13 @@
1+x =
2+ Just
3+ -- comment
4+ A
5+ { x
6+ }
7+
8+x =
9+ Just
10+ -- comment
11+ a
12+ { x
13+ }
data/examples/declaration/value/function/record/inter-comments.hsnew file+11
@@ -0,0 +1,11 @@
1+x = Just
2+ -- comment
3+ A
4+ { x
5+ }
6+
7+x = Just
8+ -- comment
9+ a
10+ { x
11+ }
data/examples/declaration/value/function/record-updaters-out.hs → data/examples/declaration/value/function/record/updaters-out.hsrenamed

The content is the same.

data/examples/declaration/value/function/record-updaters.hs → data/examples/declaration/value/function/record/updaters.hsrenamed

The content is the same.

data/examples/declaration/value/function/record-wildcards-out.hs → data/examples/declaration/value/function/record/wildcards-out.hsrenamed+7−6
@@ -3,11 +3,12 @@
33
44foo x y = Foo {x, y}
55
6−bar x y z = Bar
7− { x,
8− y,
9− z,
10− ..
11− }
6+bar x y z =
7+ Bar
8+ { x,
9+ y,
10+ z,
11+ ..
12+ }
1213
1314baz = Baz {..}
data/examples/declaration/value/function/record-wildcards.hs → data/examples/declaration/value/function/record/wildcards.hsrenamed

The content is the same.

data/examples/declaration/value/function/type-applications-and-splice-out.hsnew file+4
@@ -0,0 +1,4 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+{-# LANGUAGE TypeApplications #-}
3+
4+staticKey name = [|sing @ $(symFQN name)|]
data/examples/declaration/value/function/type-applications-and-splice.hsnew file+4
@@ -0,0 +1,4 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+{-# LANGUAGE TypeApplications #-}
3+
4+staticKey name = [| sing @ $(symFQN name) |]
data/examples/declaration/value/function/type-applications-out.hs+6
@@ -8,3 +8,9 @@ baz =
88 f @Int @String
99 a
1010 b
11+
12+goo =
13+ hash
14+ @(HASH TPraosStandardCrypto)
15+ @ByteString
16+ "And the lamb lies down on Broadway"
data/examples/declaration/value/function/type-applications.hs+5
@@ -6,3 +6,8 @@ bar = f @(Maybe Int) a b
66
77baz = f @Int @String
88 a b
9+
10+goo = hash
11+ @(HASH TPraosStandardCrypto)
12+ @ByteString
13+ "And the lamb lies down on Broadway"
data/examples/import/deduplication-bug-out.hsnew file+6
@@ -0,0 +1,6 @@
1+import Foo1 (Bar1 (..), Baz1)
2+import 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.hsnew file+6
@@ -0,0 +1,6 @@
1+import Foo1 (Bar1, Baz1, Bar1(..))
2+import 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/explicit-imports-out.hs+4−4
@@ -1,12 +1,12 @@
11import qualified MegaModule as M
2− ( (<<<),
3− (>>>),
4− Either,
2+ ( Either,
53 Maybe (Just, Nothing),
64 MaybeT (..),
7− Monad ((>>), (>>=), return),
5+ Monad (return, (>>), (>>=)),
86 MonadBaseControl,
97 join,
108 liftIO,
119 void,
10+ (<<<),
11+ (>>>),
1212 )
data/examples/import/explicit-imports-with-comments-out.hs+3−2
@@ -1,6 +1,7 @@
11import qualified MegaModule as M
22 ( -- (1)
3− (<<<), -- (2)
4− (>>>),
3+ -- (2)
54 Either, -- (3)
5+ (<<<),
6+ (>>>),
67 )
data/examples/import/merging-0-out.hsnew file+3
@@ -0,0 +1,3 @@
1+import Foo
2+import Foo (bar, foo)
3+import Foo as F
data/examples/import/merging-0.hsnew file+4
@@ -0,0 +1,4 @@
1+import Foo
2+import Foo (foo)
3+import Foo (bar)
4+import Foo as F
data/examples/import/merging-1-out.hsnew file+2
@@ -0,0 +1,2 @@
1+import "bar" Foo (bar)
2+import "foo" Foo (baz, foo)
data/examples/import/merging-1.hsnew file+3
@@ -0,0 +1,3 @@
1+import "foo" Foo (foo)
2+import "bar" Foo (bar)
3+import "foo" Foo (baz)
data/examples/import/merging-2-out.hsnew file+2
@@ -0,0 +1,2 @@
1+import Foo hiding (bar4, foo2)
2+import qualified Foo (bar3, foo1)
data/examples/import/merging-2.hsnew file+4
@@ -0,0 +1,4 @@
1+import qualified Foo (foo1)
2+import Foo hiding (foo2)
3+import qualified Foo (bar3)
4+import Foo hiding (bar4)
data/examples/import/misc-out.hs−6
@@ -1,11 +1,5 @@
11import A hiding
22 ( foobarbazqux,
3− foobarbazqux,
4− foobarbazqux,
5− foobarbazqux,
6− foobarbazqux,
7− foobarbazqux,
8− foobarbazqux,
93 )
104import {-# SOURCE #-} safe qualified Module as M hiding (a, b, c, d, e, f)
115import Name hiding ()
data/examples/import/nested-explicit-imports-out.hs+6−6
@@ -1,10 +1,10 @@
11import qualified MegaModule as M
2− ( (<<<),
3− (>>>),
4− Either,
2+ ( Either,
53 Monad
6− ( (>>),
7− (>>=),
8− return
4+ ( return,
5+ (>>),
6+ (>>=)
97 ),
8+ (<<<),
9+ (>>>),
1010 )
data/examples/import/qualified-post-out.hsnew file+5
@@ -0,0 +1,5 @@
1+{-# LANGUAGE ImportQualifiedPost #-}
2+
3+import Data.Text qualified (a, b, c)
4+import Data.Text qualified hiding (a, b, c)
5+import Data.Text qualified as T
data/examples/import/qualified-post.hsnew file+5
@@ -0,0 +1,5 @@
1+{-# LANGUAGE ImportQualifiedPost #-}
2+
3+import qualified Data.Text as T
4+import qualified Data.Text (a, c, b)
5+import Data.Text qualified hiding (c, b, a)
data/examples/import/qualified-prelude-out.hs+1−1
@@ -1,4 +1,4 @@
11module P where
22
3−import Prelude hiding ((.), id)
3+import Prelude hiding (id, (.))
44import qualified Prelude
data/examples/import/simple-out.hs+2−3
@@ -1,6 +1,5 @@
11import Data.Text
2−import Data.Text
3−import qualified Data.Text as T
4−import qualified Data.Text (a, b, c)
52import Data.Text (a, b, c)
63import Data.Text hiding (a, b, c)
4+import qualified Data.Text (a, b, c)
5+import qualified Data.Text as T
data/examples/import/simple.hs+2
@@ -4,3 +4,5 @@ import qualified Data.Text as T
44import qualified Data.Text (a, c, b)
55import Data.Text (a, b, c)
66import Data.Text hiding (c, b, a)
7+import Data.Text (a, b, c, b, a)
8+import Data.Text hiding (c, b, a, b, c)
data/examples/import/sorted-export-list-out.hsnew file+1
@@ -0,0 +1 @@
1+import Linear.Vector (Additive (..), (*^), (^*))
data/examples/import/sorted-export-list.hsnew file+1
@@ -0,0 +1 @@
1+import Linear.Vector (Additive (..), (*^), (^*))
data/examples/module-header/double-dot-with-names-out.hs+1−6
@@ -1,11 +1,6 @@
11{-# LANGUAGE PatternSynonyms #-}
22
3−module ExportSyntax
4− ( A (.., NoA),
5− Q (F, ..),
6− G (T, .., U),
7− )
8−where
3+module ExportSyntax (A (.., NoA), Q (F, ..), G (T, .., U)) where
94
105data A = A | B
116
data/examples/module-header/multiline2.hs+2−2
@@ -1,2 +1,2 @@
1−module Foo
2− (foo, bar, baz) where
1+module Foo (
2+ foo, bar, baz) where
data/examples/module-header/preceding-comment-with-haddock-out.hs+1−4
@@ -3,9 +3,6 @@
33-}
44
55-- | This is the module's Haddock.
6−module Main
7− ( main,
8− )
9−where
6+module Main (main) where
107
118main = return ()
data/examples/module-header/shebang-out.hs+1
@@ -1,4 +1,5 @@
11#! /usr/bin/env runhaskell
2+
23import Prelude
34
45main :: IO ()
data/examples/module-header/shebang-with-pragmas-out.hsnew file+5
@@ -0,0 +1,5 @@
1+#!/usr/bin/env stack
2+
3+{-# LANGUAGE OverloadedStrings #-}
4+
5+main = pure ()
data/examples/module-header/shebang-with-pragmas.hsnew file+5
@@ -0,0 +1,5 @@
1+#!/usr/bin/env stack
2+
3+{-# LANGUAGE OverloadedStrings #-}
4+
5+main = pure ()
data/examples/module-header/shebang.hs+1
@@ -1,4 +1,5 @@
11#! /usr/bin/env runhaskell
2+
23import Prelude
34
45main :: IO ()
data/examples/module-header/singleline-empty-out.hs+1−4
@@ -1,5 +1,2 @@
11-- | This demonstrates a BUG.
2−module Foo
3− (
4− )
5−where
2+module Foo () where
data/examples/module-header/stack-header-0-out.hsnew file+7
@@ -0,0 +1,7 @@
1+-- stack runhaskell
2+
3+{-# LANGUAGE OverloadedStrings #-}
4+
5+main = return ()
6+
7+-- stack runhaskell
data/examples/module-header/stack-header-0.hsnew file+4
@@ -0,0 +1,4 @@
1+-- stack runhaskell
2+{-# LANGUAGE OverloadedStrings #-}
3+main = return ()
4+-- stack runhaskell
data/examples/module-header/stack-header-1-out.hsnew file+8
@@ -0,0 +1,8 @@
1+#!/usr/bin/env stack
2+-- stack runhaskell
3+
4+{-# LANGUAGE OverloadedStrings #-}
5+
6+main = return ()
7+
8+-- stack runhaskell
data/examples/module-header/stack-header-1.hsnew file+5
@@ -0,0 +1,5 @@
1+#!/usr/bin/env stack
2+-- stack runhaskell
3+{-# LANGUAGE OverloadedStrings #-}
4+main = return ()
5+-- stack runhaskell
data/examples/module-header/stack-header-2-out.hsnew file+12
@@ -0,0 +1,12 @@
1+#!/usr/bin/env stack
2+{- stack
3+ script
4+ --resolver lts-6.25
5+ --package turtle
6+ --package "stm async"
7+ --package http-client,http-conduit
8+-}
9+
10+{-# LANGUAGE OverloadedStrings #-}
11+
12+main = return ()
data/examples/module-header/stack-header-2.hsnew file+12
@@ -0,0 +1,12 @@
1+#!/usr/bin/env stack
2+{- stack
3+ script
4+ --resolver lts-6.25
5+ --package turtle
6+ --package "stm async"
7+ --package http-client,http-conduit
8+-}
9+
10+{-# LANGUAGE OverloadedStrings #-}
11+
12+main = return ()
data/examples/module-header/warning-pragma-multiline-out.hs+1−4
@@ -1,9 +1,6 @@
11module Test
22 {-# DEPRECATED "This module is unstable" #-}
3− ( foo,
4− bar,
5− baz,
6− )
3+ (foo, bar, baz)
74where
85
96import Blah
data/examples/module-header/warning-pragma-out.hs+3−1
@@ -1,3 +1,5 @@
11module Test
2− {-# WARNING "This module is very internal" #-}
2+ {-# WARNING
3+ "This module is very internal"
4+ #-}
35where
data/examples/other/argument-comment-out.hs−1
@@ -9,7 +9,6 @@ foo ::
99 -- | Foo
1010 Int ->
1111 Int
12−
1312foo ::
1413 Foo a =>
1514 -- | Foo
data/examples/other/comment-after-preceding-haddock-out.hsnew file+12
@@ -0,0 +1,12 @@
1+module Main where
2+
3+-- | A
4+
5+-- B
6+
7+type D = E
8+
9+-- | This is 'f'
10+
11+-- * Comment
12+f :: a -> b
data/examples/other/comment-after-preceding-haddock.hsnew file+12
@@ -0,0 +1,12 @@
1+module Main where
2+
3+-- | A
4+
5+-- B
6+
7+type D = E
8+
9+-- | This is 'f'
10+
11+-- * Comment
12+f :: a -> b
data/examples/other/comment-alignment-out.hs−1
@@ -1,5 +1,4 @@
11class Foo a where
2−
32 -- | Foo.
43 foo ::
54 Int ->
data/examples/other/comment-before-hanging-out.hsnew file+5
@@ -0,0 +1,5 @@
1+x = Just
2+ -- comment
3+ do
4+ foo
5+ bar
data/examples/other/comment-before-hanging.hsnew file+5
@@ -0,0 +1,5 @@
1+x = Just
2+ -- comment
3+ do
4+ foo
5+ bar
data/examples/other/comment-glued-together-out.hsnew file+7
@@ -0,0 +1,7 @@
1+module Main (main) where
2+
3+-- | Foo.
4+
5+-- Bar
6+main :: IO ()
7+main = return ()
data/examples/other/comment-glued-together.hsnew file+6
@@ -0,0 +1,6 @@
1+module Main (main) where
2+
3+{- | Foo. -}
4+-- Bar
5+main :: IO ()
6+main = return ()
data/examples/other/comment-trailing-space-out.hsnew file+9
@@ -0,0 +1,9 @@
1+data T
2+ = {-
3+
4+ some multi-line comment
5+
6+ with empty lines
7+
8+ -}
9+ A
data/examples/other/comment-trailing-space.hsnew file+9
@@ -0,0 +1,9 @@
1+data T
2+ = {-
3+
4+ some multi-line comment
5+
6+ with empty lines
7+
8+ -}
9+ A
data/examples/other/comment-two-blocks-out.hs+2−2
@@ -2,7 +2,7 @@ newNames :: [(String, String)]
22newNames =
33 let (*) = flip (,)
44 in [ "Control" * "Monad"
5− -- Foo
5+ -- Foo
66
7− -- Bar
7+ -- Bar
88 ]
data/examples/other/cpp/continuation-out.hsnew file+8
@@ -0,0 +1,8 @@
1+foo :: Int
2+foo = 5
3+
4+#define message_for(a, b) \
5+ printf(#a " and " #b ": We love you!\n")
6+
7+bar :: Int
8+bar = 6
data/examples/other/cpp/continuation.hsnew file+8
@@ -0,0 +1,8 @@
1+foo :: Int
2+foo = 5
3+
4+#define message_for(a, b) \
5+ printf(#a " and " #b ": We love you!\n")
6+
7+bar :: Int
8+bar = 6
data/examples/other/cpp/lonely-hash-out.hsnew file+14
@@ -0,0 +1,14 @@
1+module Main (main) where
2+
3+import Data.Void
4+import Test.Hspec
5+import Test.Hspec.Megaparsec
6+import Text.Megaparsec
7+import Text.Megaparsec.Char
8+
9+# if !MIN_VERSION_base(4,13,0)
10+import Data.Semigroup ((<>))
11+# endif
12+
13+main :: IO ()
14+main = return ()
data/examples/other/cpp/lonely-hash.hsnew file+14
@@ -0,0 +1,14 @@
1+module Main (main) where
2+
3+import Data.Void
4+import Test.Hspec
5+import Test.Hspec.Megaparsec
6+import Text.Megaparsec
7+import Text.Megaparsec.Char
8+
9+# if !MIN_VERSION_base(4,13,0)
10+import Data.Semigroup ((<>))
11+# endif
12+
13+main :: IO ()
14+main = return ()
data/examples/other/cpp/separation-0a-out.hsnew file+7
@@ -0,0 +1,7 @@
1+instance Stream s => Monad (ParsecT e s m) where
2+ return = pure
3+ (>>=) = pBind
4+
5+#if !(MIN_VERSION_base(4,13,0))
6+ fail = Fail.fail
7+#endif
data/examples/other/cpp/separation-0a.hsnew file+6
@@ -0,0 +1,6 @@
1+instance Stream s => Monad (ParsecT e s m) where
2+ return = pure
3+ (>>=) = pBind
4+#if !(MIN_VERSION_base(4,13,0))
5+ fail = Fail.fail
6+#endif
data/examples/other/cpp/separation-0b-out.hsnew file+10
@@ -0,0 +1,10 @@
1+instance Stream s => Monad (ParsecT e s m) where
2+ return = pure
3+ (>>=) = pBind
4+
5+#if !(MIN_VERSION_base(4,13,0))
6+ fail = Fail.fail
7+#endif
8+
9+foo :: Int
10+foo = undefined
data/examples/other/cpp/separation-0b.hsnew file+9
@@ -0,0 +1,9 @@
1+instance Stream s => Monad (ParsecT e s m) where
2+ return = pure
3+ (>>=) = pBind
4+#if !(MIN_VERSION_base(4,13,0))
5+ fail = Fail.fail
6+#endif
7+
8+foo :: Int
9+foo = undefined
data/examples/other/cpp/separation-1a-out.hsnew file+12
@@ -0,0 +1,12 @@
1+decompressingPipe ::
2+ (PrimMonad m, MonadThrow m, MonadResource m) =>
3+ CompressionMethod ->
4+ ConduitT ByteString ByteString m ()
5+decompressingPipe Store = C.awaitForever C.yield
6+decompressingPipe Deflate = Z.decompress $ Z.WindowBits (-15)
7+
8+#ifdef ENABLE_BZIP2
9+decompressingPipe BZip2 = BZ.bunzip2
10+#else
11+decompressingPipe BZip2 = throwM BZip2Unsupported
12+#endif
data/examples/other/cpp/separation-1a.hsnew file+11
@@ -0,0 +1,11 @@
1+decompressingPipe
2+ :: (PrimMonad m, MonadThrow m, MonadResource m)
3+ => CompressionMethod
4+ -> ConduitT ByteString ByteString m ()
5+decompressingPipe Store = C.awaitForever C.yield
6+decompressingPipe Deflate = Z.decompress $ Z.WindowBits (-15)
7+#ifdef ENABLE_BZIP2
8+decompressingPipe BZip2 = BZ.bunzip2
9+#else
10+decompressingPipe BZip2 = throwM BZip2Unsupported
11+#endif
data/examples/other/cpp/separation-1b-out.hsnew file+15
@@ -0,0 +1,15 @@
1+decompressingPipe ::
2+ (PrimMonad m, MonadThrow m, MonadResource m) =>
3+ CompressionMethod ->
4+ ConduitT ByteString ByteString m ()
5+decompressingPipe Store = C.awaitForever C.yield
6+decompressingPipe Deflate = Z.decompress $ Z.WindowBits (-15)
7+
8+#ifdef ENABLE_BZIP2
9+decompressingPipe BZip2 = BZ.bunzip2
10+#else
11+decompressingPipe BZip2 = throwM BZip2Unsupported
12+#endif
13+
14+foo :: Int
15+foo = undefined
data/examples/other/cpp/separation-1b.hsnew file+14
@@ -0,0 +1,14 @@
1+decompressingPipe
2+ :: (PrimMonad m, MonadThrow m, MonadResource m)
3+ => CompressionMethod
4+ -> ConduitT ByteString ByteString m ()
5+decompressingPipe Store = C.awaitForever C.yield
6+decompressingPipe Deflate = Z.decompress $ Z.WindowBits (-15)
7+#ifdef ENABLE_BZIP2
8+decompressingPipe BZip2 = BZ.bunzip2
9+#else
10+decompressingPipe BZip2 = throwM BZip2Unsupported
11+#endif
12+
13+foo :: Int
14+foo = undefined
data/examples/other/cpp/separation-2a-out.hsnew file+9
@@ -0,0 +1,9 @@
1+ffff, ffffffff :: Natural
2+
3+#ifdef HASKELL_ZIP_DEV_MODE
4+ffff = 200
5+ffffffff = 5000
6+#else
7+ffff = 0xffff
8+ffffffff = 0xffffffff
9+#endif
data/examples/other/cpp/separation-2a.hsnew file+8
@@ -0,0 +1,8 @@
1+ffff, ffffffff :: Natural
2+#ifdef HASKELL_ZIP_DEV_MODE
3+ffff = 200
4+ffffffff = 5000
5+#else
6+ffff = 0xffff
7+ffffffff = 0xffffffff
8+#endif
data/examples/other/cpp/separation-2b-out.hsnew file+11
@@ -0,0 +1,11 @@
1+ffff, ffffffff :: Natural
2+#ifdef HASKELL_ZIP_DEV_MODE
3+ffff = 200
4+ffffffff = 5000
5+#else
6+ffff = 0xffff
7+ffffffff = 0xffffffff
8+#endif
9+
10+foo :: Int
11+foo = undefined
data/examples/other/cpp/separation-2b.hsnew file+11
@@ -0,0 +1,11 @@
1+ffff, ffffffff :: Natural
2+#ifdef HASKELL_ZIP_DEV_MODE
3+ffff = 200
4+ffffffff = 5000
5+#else
6+ffff = 0xffff
7+ffffffff = 0xffffffff
8+#endif
9+
10+foo :: Int
11+foo = undefined
data/examples/other/cpp/shifted-out.hsnew file+21
@@ -0,0 +1,21 @@
1+sinkData h compression = do
2+ (uncompressedSize, crc32, compressedSize) <-
3+ case compression of
4+ Store ->
5+ withCompression
6+ dataSink
7+ Deflate ->
8+ withCompression $
9+ Z.compress 9 (Z.WindowBits (-15)) .| dataSink
10+#ifdef ENABLE_BZIP2
11+ BZip2 -> withCompression $
12+ BZ.bzip2 .| dataSink
13+#else
14+ BZip2 -> throwM BZip2Unsupported
15+#endif
16+ return
17+ DataDescriptor
18+ { ddCRC32 = fromIntegral crc32,
19+ ddCompressedSize = compressedSize,
20+ ddUncompressedSize = uncompressedSize
21+ }
data/examples/other/cpp/shifted.hsnew file+17
@@ -0,0 +1,17 @@
1+sinkData h compression = do
2+ (uncompressedSize, crc32, compressedSize) <-
3+ case compression of
4+ Store -> withCompression
5+ dataSink
6+ Deflate -> withCompression $
7+ Z.compress 9 (Z.WindowBits (-15)) .| dataSink
8+#ifdef ENABLE_BZIP2
9+ BZip2 -> withCompression $
10+ BZ.bzip2 .| dataSink
11+#else
12+ BZip2 -> throwM BZip2Unsupported
13+#endif
14+ return DataDescriptor
15+ { ddCRC32 = fromIntegral crc32
16+ , ddCompressedSize = compressedSize
17+ , ddUncompressedSize = uncompressedSize }
data/examples/other/cpp/simple-import-out.hsnew file+14
@@ -0,0 +1,14 @@
1+module Main (main) where
2+
3+import Data.Void
4+import Test.Hspec
5+import Test.Hspec.Megaparsec
6+import Text.Megaparsec
7+import Text.Megaparsec.Char
8+
9+#if !MIN_VERSION_base(4,13,0)
10+import Data.Semigroup ((<>))
11+#endif
12+
13+main :: IO ()
14+main = return ()
data/examples/other/cpp/simple-import.hsnew file+14
@@ -0,0 +1,14 @@
1+module Main (main) where
2+
3+import Data.Void
4+import Test.Hspec
5+import Test.Hspec.Megaparsec
6+import Text.Megaparsec
7+import Text.Megaparsec.Char
8+
9+#if !MIN_VERSION_base(4,13,0)
10+import Data.Semigroup ((<>))
11+#endif
12+
13+main :: IO ()
14+main = return ()
data/examples/other/disabling/indented-fragment-out.hsnew file+7
@@ -0,0 +1,7 @@
1+foo =
2+ {- ORMOLU_DISABLE -}
3+ testCase "Foo" testFoo :
4+ testCase "Bar" testBar :
5+ testCase "Baz" testBaz :
6+ {- ORMOLU_ENABLE -}
7+ []
data/examples/other/disabling/indented-fragment.hsnew file+7
@@ -0,0 +1,7 @@
1+foo =
2+ {- ORMOLU_DISABLE -}
3+ testCase "Foo" testFoo :
4+ testCase "Bar" testBar :
5+ testCase "Baz" testBaz :
6+ {- ORMOLU_ENABLE -}
7+ []
data/examples/other/disabling/single-definition-out.hsnew file+10
@@ -0,0 +1,10 @@
1+module Foo (foo, bar) where
2+
3+{- ORMOLU_DISABLE -}
4+foo :: Int -> Int
5+foo = (+5)
6+{- ORMOLU_ENABLE -}
7+
8+bar :: Bool -> Bool
9+bar True = True
10+bar False = True
data/examples/other/disabling/single-definition.hsnew file+10
@@ -0,0 +1,10 @@
1+module Foo (foo,bar) where
2+
3+{- ORMOLU_DISABLE -}
4+foo :: Int -> Int
5+foo = (+5)
6+{- ORMOLU_ENABLE -}
7+
8+bar :: Bool -> Bool
9+bar True = True
10+bar False = True
data/examples/other/disabling/whole-file-out.hsnew file+10
@@ -0,0 +1,10 @@
1+{- ORMOLU_DISABLE -}
2+
3+module Foo (foo,bar) where
4+
5+foo :: Int -> Int
6+foo = (+5)
7+
8+bar :: Bool -> Bool
9+bar True = True
10+bar False = True
data/examples/other/disabling/whole-file.hsnew file+10
@@ -0,0 +1,10 @@
1+{- ORMOLU_DISABLE -}
2+
3+module Foo (foo,bar) where
4+
5+foo :: Int -> Int
6+foo = (+5)
7+
8+bar :: Bool -> Bool
9+bar True = True
10+bar False = True
data/examples/other/empty-forall-out.hsnew file+18
@@ -0,0 +1,18 @@
1+-- Empty foralls are handled correctly in different situations.
2+
3+data D = forall. D Int
4+
5+data G where
6+ G :: forall. Int -> G
7+
8+f :: forall. a -> a
9+f x = x
10+
11+type family T x where
12+ forall. T x = x
13+
14+{-# RULES
15+"r"
16+ r a =
17+ ()
18+ #-}
data/examples/other/empty-forall.hsnew file+17
@@ -0,0 +1,17 @@
1+-- Empty foralls are handled correctly in different situations.
2+
3+data D = forall. D Int
4+
5+data G where
6+ G :: forall. Int -> G
7+
8+f :: forall. a -> a
9+f x = x
10+
11+type family T x where
12+ forall. T x = x
13+
14+{-# RULES
15+ "r"
16+ forall. r a = ()
17+#-}
data/examples/other/inline-comment-0-out.hsnew file+3
@@ -0,0 +1,3 @@
1+x = ({-a-} b, c)
2+
3+y = ({-a-} b)
data/examples/other/inline-comment-0.hsnew file+3
@@ -0,0 +1,3 @@
1+x = ({-a-}b, c)
2+
3+y = ({-a-}b)
data/examples/other/inline-comment-1-out.hsnew file+12
@@ -0,0 +1,12 @@
1+showPs env ((n, _, Let _ t v) : bs) =
2+ " " ++ show n ++ " : "
3+ ++ showEnv env ({- normalise ctxt env -} t)
4+ ++ " = "
5+ ++ showEnv env ({- normalise ctxt env -} v)
6+ ++ "\n"
7+ ++ showPs env bs
8+showPs env ((n, _, b) : bs) =
9+ " " ++ show n ++ " : "
10+ ++ showEnv env ({- normalise ctxt env -} (binderTy b))
11+ ++ "\n"
12+ ++ showPs env bs
data/examples/other/inline-comment-1.hsnew file+9
@@ -0,0 +1,9 @@
1+showPs env ((n, _, Let _ t v):bs)
2+ = " " ++ show n ++ " : " ++
3+ showEnv env ({- normalise ctxt env -} t) ++ " = " ++
4+ showEnv env ({- normalise ctxt env -} v) ++
5+ "\n" ++ showPs env bs
6+showPs env ((n, _, b):bs)
7+ = " " ++ show n ++ " : " ++
8+ showEnv env ({- normalise ctxt env -} (binderTy b)) ++
9+ "\n" ++ showPs env bs
data/examples/other/multiline-forall-out.hs+1−2
@@ -12,8 +12,7 @@ data D
1212 )
1313 (x :: *)
1414 (y :: *).
15− D
16− (f x y)
15+ D (f x y)
1716
1817data G where
1918 G ::
data/examples/other/multiple-blank-line-comment-out.hsnew file+9
@@ -0,0 +1,9 @@
1+module A where
2+
3+a =
4+ b
5+ [ f
6+ {-,
7+
8+ -}
9+ ]
data/examples/other/multiple-blank-line-comment.hsnew file+10
@@ -0,0 +1,10 @@
1+module A where
2+
3+a =
4+ b
5+ [ f
6+ {-,
7+
8+
9+ -}
10+ ]
data/examples/other/pragma-comments-after-out.hsnew file+4
@@ -0,0 +1,4 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+-- TODO: Fix and delete this pragma
4+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
data/examples/other/pragma-comments-after.hsnew file+3
@@ -0,0 +1,3 @@
1+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} -- TODO: Fix and delete this pragma
2+{-# LANGUAGE AllowAmbiguousTypes #-}
3+{-# LANGUAGE ConstraintKinds #-}
data/examples/other/pragma-comments-out.hsnew file+8
@@ -0,0 +1,8 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+-- TODO This extension is probably too dangerous, remove it.
3+{-# LANGUAGE RecordWildCards #-}
4+-- Avoid warning produced by TH.
5+{-# OPTIONS_GHC -fno-warn-overlapping-patterns #-}
6+
7+-- | Header comment.
8+module Foo () where
data/examples/other/pragma-comments.hsnew file+11
@@ -0,0 +1,11 @@
1+-- | Header comment.
2+
3+{-# LANGUAGE OverloadedStrings #-}
4+
5+-- Avoid warning produced by TH.
6+{-# OPTIONS_GHC -fno-warn-overlapping-patterns #-}
7+
8+-- TODO This extension is probably too dangerous, remove it.
9+{-# LANGUAGE RecordWildCards #-}
10+
11+module Foo () where
data/examples/other/pragma-out.hs+1−4
@@ -9,7 +9,4 @@
99{-# OPTIONS_HADDOCK prune, show-extensions #-}
1010
1111-- | Header comment.
12−module Foo
13− (
14− )
15−where
12+module Foo () where
data/examples/other/pragma-sorting-out.hsnew file+11
@@ -0,0 +1,11 @@
1+{-# LANGUAGE NondecreasingIndentation #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{-# LANGUAGE TypeFamilies #-}
4+-- This gap is necessary for stylish Haskell not to re-arrange
5+-- NoMonoLocalBinds before TypeFamilies
6+{-# LANGUAGE NoMonoLocalBinds #-}
7+
8+module Foo
9+ ( bar,
10+ )
11+where
data/examples/other/pragma-sorting.hsnew file+10
@@ -0,0 +1,10 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE TypeFamilies #-}
3+{-# LANGUAGE NondecreasingIndentation #-}
4+-- This gap is necessary for stylish Haskell not to re-arrange
5+-- NoMonoLocalBinds before TypeFamilies
6+{-# LANGUAGE NoMonoLocalBinds #-}
7+
8+module Foo (
9+ bar
10+ ) where
default.nix+108−50
@@ -1,7 +1,9 @@
1−{ pkgs ? (import ./nix/nixpkgs) }:
1+{ pkgs ? (import ./nix/nixpkgs { inherit system; })
2+, system ? builtins.currentSystem
3+, ormoluCompiler ? "ghc884" # the shell doesn't work with ghc8101 yet
4+}:
25
36let
4− ormoluCompiler = "ghc865";
57 source = pkgs.lib.sourceByRegex ./. [
68 "^.*\.md$"
79 "^app.*$"
@@ -14,56 +16,71 @@ let
1416 overrides = ormoluOverlay;
1517 };
1618 ormoluOverlay = self: super: {
17− "ormolu" = super.callCabal2nix "ormolu" source { };
18− # Nixpkgs provides ghc-lib-parser-8.8.0.20190424, but we want
19− # ghc-lib-parser-8.8.1. We disable Haddock generation because it's way
20− # too slow.
21− "ghc-lib-parser" = pkgs.haskell.lib.dontHaddock
22− (super.callHackage "ghc-lib-parser" "8.8.1" { });
19+ "ormolu" = super.callCabal2nixWithOptions "ormolu" source "-fdev" { };
2320 };
2421 ormolize = import ./nix/ormolize {
2522 inherit pkgs;
2623 inherit haskellPackages;
2724 };
28− # NOTE We have to exclude some directories for some packages because
29− # Ormolu needs files to be parsable by Haddock which is not always the
30− # case. For example some tests and examples do not parse.
31− excludedHackageDirs = {
32− "aws" = ["Examples"];
33− "distributed-process" = ["benchmarks"];
34− "esqueleto" = ["test"];
35− "fay" = ["examples"];
36− "postgrest" = ["test"];
37− };
25+ expectedFailures = [
26+ "Agda"
27+ "aws"
28+ "distributed-process"
29+ "esqueleto"
30+ "fay"
31+ "hlint"
32+ "idris"
33+ "intero"
34+ "leksah"
35+ "pandoc"
36+ "pipes"
37+ "postgrest"
38+ "purescript"
39+ "tensorflow"
40+ ] ++ (if ormoluCompiler == "ghc8101" then [] else ["lens"]);
3841 ormolizedPackages = doCheck:
3942 pkgs.lib.mapAttrs (name: p: ormolize {
4043 package = p;
4144 inherit doCheck;
42− excludedDirs =
43− if builtins.hasAttr name excludedHackageDirs
44− then excludedHackageDirs.${name}
45− else [];
45+ expectedFailures =
46+ if pkgs.lib.lists.any (x: x == name) expectedFailures
47+ then ./expected-failures + "/${name}.txt"
48+ else null;
4649 }) haskellPackages;
4750in {
4851 ormolu = haskellPackages.ormolu;
49− ormoluShell = haskellPackages.shellFor {
50− packages = ps: [
51− ps.ormolu
52− ];
53− buildInputs = [
54− haskellPackages.cabal-install
55− haskellPackages.ghcid
56− ];
52+ # We put the derivations in another attribute set to avoid building them
53+ # when nix-build is run.
54+ dev = {
55+ ormoluShell =
56+ haskellPackages.shellFor {
57+ packages = ps: [
58+ ps.ormolu
59+ ];
60+ buildInputs = [
61+ haskellPackages.cabal-install
62+ haskellPackages.ghcid
63+ ];
64+ };
65+ withOrmolu = haskellPackages.shellFor {
66+ packages = ps: [];
67+ buildInputs = [
68+ haskellPackages.cabal-install
69+ haskellPackages.ormolu
70+ ];
71+ };
5772 };
5873 inherit ormoluOverlay ormoluCompiler;
5974 hackage = ormolizedPackages false;
6075 hackageTests = with pkgs.lib; pkgs.recurseIntoAttrs (
6176 let ps = [
77+ "Agda"
6278 "QuickCheck"
6379 "ShellCheck"
6480 "aeson"
6581 "attoparsec"
6682 "aws"
83+ "brick"
6784 "capability"
6885 "cassava"
6986 "conduit"
@@ -75,37 +92,78 @@ in {
7592 "hakyll"
7693 "haxl"
7794 "hedgehog"
95+ "hledger"
7896 "hlint"
97+ "http-client"
98+ "idris"
99+ "intero"
100+ "leksah"
101+ "lens"
79102 "megaparsec"
80− "ormolu"
81103 "optics"
104+ "ormolu"
105+ "pandoc"
106+ "parsec3"
107+ "pipes"
82108 "postgrest"
109+ "purescript"
83110 "servant"
84111 "servant-server"
112+ "stack"
85113 "tensorflow"
86114 "text_1_2_4_0"
87115 "tls"
88116 "yesod-core"
89−
90− # Comment idempotence issue
91−
92− # "Agda"
93− # "brick"
94− # "hledger"
95− # "http-client"
96− # "idris"
97− # "intero"
98− # "leksah"
99− # "pandoc"
100− # "pipes"
101− # "stack"
102−
103− # Missing language extension
104−
105− # "lens" #fixed in master
106− # "purescript"
107−
108117 ];
109118 in listToAttrs (map (p: nameValuePair p (ormolizedPackages true).${p}) ps)
110119 );
120+ regionTests = pkgs.stdenv.mkDerivation {
121+ name = "ormolu-region-tests";
122+ src = ./region-tests;
123+ buildInputs = [
124+ haskellPackages.ormolu
125+ pkgs.diffutils
126+ ];
127+ doCheck = true;
128+ buildPhase = ''
129+ cp src.hs result-all-implicit.hs
130+ ormolu --check-idempotence --mode inplace result-all-implicit.hs
131+ cp src.hs result-all-explicit.hs
132+ ormolu --check-idempotence --mode inplace --start-line 1 --end-line 18 result-all-explicit.hs
133+ cp src.hs result-only-start.hs
134+ ormolu --check-idempotence --mode inplace --start-line 1 result-only-start.hs
135+ cp src.hs result-only-end.hs
136+ ormolu --check-idempotence --mode inplace --end-line 18 result-only-end.hs
137+ cp src.hs result-6-7.hs
138+ ormolu --check-idempotence --mode inplace --start-line 6 --end-line 7 result-6-7.hs
139+ cp src.hs result-6-8.hs
140+ ormolu --check-idempotence --mode inplace --start-line 6 --end-line 8 result-6-8.hs
141+ cp src.hs result-9-12.hs
142+ ormolu --check-idempotence --mode inplace --start-line 9 --end-line 12 result-9-12.hs
143+ cp src.hs result-17-18.hs
144+ ormolu --check-idempotence --mode inplace --start-line 17 --end-line 18 result-17-18.hs
145+ '';
146+ checkPhase = ''
147+ echo result-all-implicit.hs
148+ diff --color=always expected-result-all.hs result-all-implicit.hs
149+ echo result-all-explicit.hs
150+ diff --color=always expected-result-all.hs result-all-explicit.hs
151+ echo result-only-start.hs
152+ diff --color=always expected-result-all.hs result-only-start.hs
153+ echo result-only-end.hs
154+ diff --color=always expected-result-all.hs result-only-end.hs
155+ echo result-6-7.hs
156+ diff --color=always expected-result-6-7.hs result-6-7.hs
157+ echo result-6-8.hs
158+ diff --color=always expected-result-6-8.hs result-6-8.hs
159+ echo result-9-12.hs
160+ diff --color=always expected-result-9-12.hs result-9-12.hs
161+ echo result-17-18.hs
162+ diff --color=always expected-result-17-18.hs result-17-18.hs
163+ '';
164+ installPhase = ''
165+ mkdir "$out"
166+ find . -name '*.hs' -exec cp --parents {} $out \;
167+ '';
168+ };
111169}
expected-failures/Agda.txtnew file+11
@@ -0,0 +1,11 @@
1+Formatting is not idempotent:
2+ dist/build/Agda/Syntax/Parser/Lexer.hs<rendered>:1602:17
3+ before: " = (check_ac"
4+ after: " =\n "
5+Please, consider reporting the bug.
6+
7+Formatting is not idempotent:
8+ src/full/Agda/Syntax/Translation/InternalToAbstract.hs<rendered>:769:4
9+ before: " nes\n\n "
10+ after: " nes\n\n -- Andreas"
11+Please, consider reporting the bug.
expected-failures/aws.txtnew file+9
@@ -0,0 +1,9 @@
1+The GHC parser (in Haddock mode) failed:
2+ Examples/PutBucketNearLine.hs:5:1-6
3+ parse error on input `import'
4+
5+The GHC parser (in Haddock mode) failed:
6+ Examples/Sqs.hs:(88,3)-(90,4)
7+ parse error on input `{- | Let's make sure the queue was actually deleted and that the same number of queues exist at when
8+ | the program ends as when it started.
9+ -}'
expected-failures/distributed-process.txtnew file+7
@@ -0,0 +1,7 @@
1+The GHC parser (in Haddock mode) failed:
2+ benchmarks/Channels.hs:2:1-6
3+ parse error on input `import'
4+
5+The GHC parser (in Haddock mode) failed:
6+ benchmarks/Spawns.hs:5:1-6
7+ parse error on input `import'
expected-failures/esqueleto.txtnew file+7
@@ -0,0 +1,7 @@
1+The GHC parser (in Haddock mode) failed:
2+ src/Database/Esqueleto/Internal/Internal.hs:379:1
3+ lexical error in string/character literal at character 's'
4+
5+The GHC parser (in Haddock mode) failed:
6+ test/PostgreSQL/Test.hs:562:9-41
7+ parse error on input `-- | Check the result is not null'
expected-failures/fay.txtnew file+18
@@ -0,0 +1,18 @@
1+The GHC parser (in Haddock mode) failed:
2+ examples/Separated.hs:12:1-6
3+ parse error on input `module'
4+
5+The GHC parser (in Haddock mode) failed:
6+ examples/calc.hs:(4,1)-(6,2)
7+ parse error on input `-- $ fay -p --html-wrapper --html-js-lib jquery.min.js examples/calc.hs
8+-- You also need to download jquery.min.js.
9+--'
10+
11+The GHC parser (in Haddock mode) failed:
12+ examples/canvaswater.hs:(6,1)-(7,17)
13+ parse error on input `-- | A demonstration of Fay using the canvas element to display a
14+-- simple effect.'
15+
16+The GHC parser (in Haddock mode) failed:
17+ examples/data.hs:9:1-6
18+ parse error on input `module'
expected-failures/hlint.txtnew file+11
@@ -0,0 +1,11 @@
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.txtnew file+7
@@ -0,0 +1,7 @@
1+The GHC parser (in Haddock mode) failed:
2+ src/Idris/Parser.hs:1052:1
3+ parse error on input `@'
4+
5+The GHC parser (in Haddock mode) failed:
6+ src/Idris/Parser/Expr.hs:75:1
7+ parse error on input `@'
expected-failures/intero.txtnew file+5
@@ -0,0 +1,5 @@
1+Formatting is not idempotent:
2+ src/InteractiveUI.hs<rendered>:3748:33
3+ before: "text \"Try\" <+> doWha"
4+ after: "text \"Try\"\n "
5+Please, consider reporting the bug.
expected-failures/leksah.txtnew file+5
@@ -0,0 +1,5 @@
1+Formatting is not idempotent:
2+ src/IDE/Pane/Modules.hs<rendered>:1184:7
3+ before: "cr\n -- show"
4+ after: "cr\n in -- show"
5+Please, consider reporting the bug.
expected-failures/lens.txtnew file+3
@@ -0,0 +1,3 @@
1+The GHC parser (in Haddock mode) failed:
2+ src/Control/Exception/Lens.hs:180:13-37
3+ parse error on input `AllocationLimitExceeded__'
expected-failures/pandoc.txtnew file+9
@@ -0,0 +1,9 @@
1+The GHC parser (in Haddock mode) failed:
2+ benchmark/weigh-pandoc.hs:14:1-6
3+ parse error on input `import'
4+
5+Formatting is not idempotent:
6+ src/Text/Pandoc/Readers/Vimwiki.hs<rendered>:613:19
7+ before: ".softbreak <$ endlin"
8+ after: ".softbreak\n <$ en"
9+Please, consider reporting the bug.
expected-failures/pipes.txtnew file+3
@@ -0,0 +1,3 @@
1+AST of input and AST of formatted code differ.
2+ at src/Pipes/Core.hs:(128,1)-(151,2)
3+Please, consider reporting the bug.
expected-failures/postgrest.txtnew file+5
@@ -0,0 +1,5 @@
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.txtnew file+18
@@ -0,0 +1,18 @@
1+The GHC parser (in Haddock mode) failed:
2+ src/Language/PureScript/AST/Declarations.hs:479:1-17
3+ Invalid type signature: pattern ValueDecl :: ...
4+Perhaps you meant to use PatternSynonyms?
5+
6+The GHC parser (in Haddock mode) failed:
7+ src/Language/PureScript/AST/SourcePos.hs:99:1-22
8+ Invalid type signature: pattern NullSourceSpan :: ...
9+Perhaps you meant to use PatternSynonyms?
10+
11+The GHC parser (in Haddock mode) failed:
12+ src/Language/PureScript/Constants.hs:42:1-15
13+ Invalid type signature: pattern Discard :: ...
14+Perhaps you meant to use PatternSynonyms?
15+
16+The GHC parser (in Haddock mode) failed:
17+ src/Language/PureScript/Crash.hs:16:1-6
18+ parse error on input `import'
expected-failures/tensorflow.txtnew file+7
@@ -0,0 +1,7 @@
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'
format.sh+6−3
@@ -1,5 +1,5 @@
11#!/usr/bin/env nix-shell
2−#!nix-shell -p "(import ./default.nix {}).ormolu" -i bash --pure
2+#!nix-shell default.nix -A dev.withOrmolu -i bash --pure
33#
44# Format Ormolu using current version of Ormolu.
55
@@ -7,5 +7,8 @@ set -e
77
88export LANG="C.UTF-8"
99
10−ormolu --mode inplace $(find src -type f \( -name "*.hs" -o -name "*.hs-boot" \))
11−ormolu --mode inplace $(find tests -type f -name "*.hs")
10+cabal format
11+
12+ormolu -m inplace $(find app -type f -name "*.hs")
13+ormolu -m inplace $(find src -type f \( -name "*.hs" -o -name "*.hs-boot" \))
14+ormolu -m inplace $(find tests -type f -name "*.hs")
nix/nixpkgs/default.nix+7−3
@@ -1,9 +1,13 @@
1+{ system ? builtins.currentSystem }:
12let
2− rev = "19.09";
3− sha256 = "0mhqhq21y5vrr1f30qd2bvydv4bbbslvyzclhw0kdxmkgg3z4c92";
3+ rev = "32b46dd897ab2143a609988a04d87452f0bbef59";
4+ sha256 = "1gzfrpjnr1bz9zljsyg3a4zrhk8r927sz761mrgcg56dwinkhpjk";
45 nixpkgs = builtins.fetchTarball {
56 url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
67 inherit sha256;
78 };
8− pkgs = import nixpkgs { config.allowUnfree = true; };
9+ pkgs = import nixpkgs {
10+ inherit system;
11+ config.allowUnfree = true;
12+ };
913in pkgs
nix/ormolize/default.nix+29−13
@@ -2,32 +2,48 @@
22, haskellPackages
33}:
44{ package
5+, expectedFailures ? null
56, doCheck ? false
6−, excludedDirs ? []
77}:
88 pkgs.stdenv.mkDerivation rec {
99 name = package.name + "-ormolized";
1010 src = package.src;
1111 buildInputs = [
1212 haskellPackages.cpphs
13− (if doCheck
14− then haskellPackages.ormolu
15− else pkgs.haskell.lib.dontCheck haskellPackages.ormolu)
13+ haskellPackages.ormolu
14+ pkgs.diffutils
1615 pkgs.glibcLocales
1716 ];
1817 LANG = "en_US.UTF-8";
19− excludedDirsRendered =
20− if excludedDirs == []
21− then ""
22− else pkgs.lib.concatMapStringsSep " " (x: "-not -path './" + x + "/*'") excludedDirs;
2318 buildPhase = ''
24− find . -name '*.hs' ${excludedDirsRendered} -exec bash ${./ormolize.sh} {} \; 2> log.txt
25− cat log.txt
19+ hs_files=$(find . -name '*.hs')
20+ for hs_file in $hs_files; do
21+
22+ # drop includes
23+ sed -i '/^#include/d' "$hs_file"
24+
25+ # deal with CPP
26+ cpphs "$hs_file" --noline -DARCH_X86 > "''${hs_file}-nocpp" 2> /dev/null
27+
28+ # annoyingly, cpphs cannot modify files in place
29+ mv "''${hs_file}-nocpp" "$hs_file"
30+
31+ # preserve the original
32+ cp "$hs_file" "''${hs_file}-original"
33+ done
34+
35+ (ormolu --check-idempotence --mode inplace $hs_files || true) 2> log.txt
2636 '';
2737 inherit doCheck;
28− checkPhase = ''
29− if [[ -s log.txt ]]; then exit 1; fi
30− '';
38+ checkPhase =
39+ if expectedFailures == null
40+ then ''
41+ echo "No failures expected"
42+ if [[ -s log.txt ]]; then exit 1; fi
43+ ''
44+ else ''
45+ diff --ignore-blank-lines --color=always ${expectedFailures} log.txt
46+ '';
3147 installPhase = ''
3248 mkdir "$out"
3349 find . -name '*.hs-original' -exec cp --parents {} $out \;
nix/ormolize/ormolize.shdeleted−17
@@ -1,17 +0,0 @@
1−set -euo pipefail
2−
3−# drop includes
4−sed -i '/^#include/d' "$1"
5−
6−# deal with CPP in a fairly straightforward way
7−cpphs "$1" --noline > "$1-nocpp" 2> /dev/null
8−
9−# annoyingly, cpphs cannot modify files in place
10−mv "$1-nocpp" "$1"
11−
12−# preserve the original
13−cp "$1" "$1-original"
14−
15−# run ormolu
16−ormolu --tolerate-cpp --check-idempotency --mode inplace "$1" ||
17− ormolu --tolerate-cpp --unsafe --mode inplace "$1"
ormolu.cabal+174−155
@@ -1,165 +1,184 @@
1−name: ormolu
2−version: 0.0.1.0
3−cabal-version: 1.18
4−tested-with: GHC==8.6.5
5−license: BSD3
6−license-file: LICENSE.md
7−maintainer: Mark Karpov <mark.karpov@tweag.io>
8−homepage: https://github.com/tweag/ormolu
9−bug-reports: https://github.com/tweag/ormolu/issues
10−category: Development, Formatting
11−synopsis: A formatter for Haskell source code
12−build-type: Simple
13−description: A formatter for Haskell source code.
14−extra-doc-files: CONTRIBUTING.md
15− , CHANGELOG.md
16− , DESIGN.md
17− , README.md
18−data-files: data/examples/declaration/annotation/*.hs
19− , data/examples/declaration/class/*.hs
20− , data/examples/declaration/data/*.hs
21− , data/examples/declaration/data/gadt/*.hs
22− , data/examples/declaration/default/*.hs
23− , data/examples/declaration/deriving/*.hs
24− , data/examples/declaration/foreign/*.hs
25− , data/examples/declaration/instance/*.hs
26− , data/examples/declaration/rewrite-rule/*.hs
27− , data/examples/declaration/role-annotation/*.hs
28− , data/examples/declaration/signature/complete/*.hs
29− , data/examples/declaration/signature/fixity/*.hs
30− , data/examples/declaration/signature/inline/*.hs
31− , data/examples/declaration/signature/minimal/*.hs
32− , data/examples/declaration/signature/pattern/*.hs
33− , data/examples/declaration/signature/set-cost-centre/*.hs
34− , data/examples/declaration/signature/specialize/*.hs
35− , data/examples/declaration/signature/type/*.hs
36− , data/examples/declaration/splice/*.hs
37− , data/examples/declaration/type-families/closed-type-family/*.hs
38− , data/examples/declaration/type-families/data-family/*.hs
39− , data/examples/declaration/type-families/type-family/*.hs
40− , data/examples/declaration/type-synonyms/*.hs
41− , data/examples/declaration/type/*.hs
42− , data/examples/declaration/value/function/*.hs
43− , data/examples/declaration/value/function/arrow/*.hs
44− , data/examples/declaration/value/function/comprehension/*.hs
45− , data/examples/declaration/value/function/do/*.hs
46− , data/examples/declaration/value/function/infix/*.hs
47− , data/examples/declaration/value/function/pattern/*.hs
48− , data/examples/declaration/value/other/*.hs
49− , data/examples/declaration/value/pattern-synonyms/*.hs
50− , data/examples/declaration/warning/*.hs
51− , data/examples/import/*.hs
52− , data/examples/module-header/*.hs
53− , data/examples/other/*.hs
1+cabal-version: 1.18
2+name: ormolu
3+version: 0.1.2.0
4+license: BSD3
5+license-file: LICENSE.md
6+maintainer: Mark Karpov <mark.karpov@tweag.io>
7+tested-with: ghc ==8.6.5 ghc ==8.8.4 ghc ==8.10.1
8+homepage: https://github.com/tweag/ormolu
9+bug-reports: https://github.com/tweag/ormolu/issues
10+synopsis: A formatter for Haskell source code
11+description: A formatter for Haskell source code.
12+category: Development, Formatting
13+build-type: Simple
14+data-files:
15+ data/examples/declaration/annotation/*.hs
16+ data/examples/declaration/class/*.hs
17+ data/examples/declaration/data/*.hs
18+ data/examples/declaration/data/gadt/*.hs
19+ data/examples/declaration/default/*.hs
20+ data/examples/declaration/deriving/*.hs
21+ data/examples/declaration/foreign/*.hs
22+ data/examples/declaration/instance/*.hs
23+ data/examples/declaration/rewrite-rule/*.hs
24+ data/examples/declaration/role-annotation/*.hs
25+ data/examples/declaration/signature/complete/*.hs
26+ data/examples/declaration/signature/fixity/*.hs
27+ data/examples/declaration/signature/inline/*.hs
28+ data/examples/declaration/signature/minimal/*.hs
29+ data/examples/declaration/signature/pattern/*.hs
30+ data/examples/declaration/signature/set-cost-centre/*.hs
31+ data/examples/declaration/signature/specialize/*.hs
32+ data/examples/declaration/signature/type/*.hs
33+ data/examples/declaration/splice/*.hs
34+ data/examples/declaration/type-families/closed-type-family/*.hs
35+ data/examples/declaration/type-families/data-family/*.hs
36+ data/examples/declaration/type-families/type-family/*.hs
37+ data/examples/declaration/type-synonyms/*.hs
38+ data/examples/declaration/type/*.hs
39+ data/examples/declaration/value/function/*.hs
40+ data/examples/declaration/value/function/arrow/*.hs
41+ data/examples/declaration/value/function/comprehension/*.hs
42+ data/examples/declaration/value/function/do/*.hs
43+ data/examples/declaration/value/function/infix/*.hs
44+ data/examples/declaration/value/function/pattern/*.hs
45+ data/examples/declaration/value/other/*.hs
46+ data/examples/declaration/value/pattern-synonyms/*.hs
47+ data/examples/declaration/warning/*.hs
48+ data/examples/import/*.hs
49+ data/examples/module-header/*.hs
50+ data/examples/other/*.hs
51+
52+extra-doc-files:
53+ CONTRIBUTING.md
54+ CHANGELOG.md
55+ DESIGN.md
56+ README.md
5457
5558source-repository head
56− type: git
57− location: https://github.com/tweag/ormolu.git
59+ type: git
60+ location: https://github.com/tweag/ormolu.git
5861
5962flag dev
60− description: Turn on development settings.
61− manual: True
62− default: False
63+ description: Turn on development settings.
64+ default: False
65+ manual: True
6366
6467library
65− hs-source-dirs: src
66− build-depends: base >= 4.12 && < 5.0
67− , bytestring >= 0.2 && < 0.11
68− , containers >= 0.5 && < 0.7
69− , dlist >= 0.8 && < 0.9
70− , exceptions >= 0.6 && < 0.11
71− , ghc-lib-parser == 8.8.1
72− , mtl >= 2.0 && < 3.0
73− , syb >= 0.7 && < 0.8
74− , text >= 0.2 && < 1.3
75− exposed-modules: Ormolu
76− , Ormolu.Config
77− , Ormolu.Diff
78− , Ormolu.Exception
79− , Ormolu.Imports
80− , Ormolu.Parser
81− , Ormolu.Parser.Anns
82− , Ormolu.Parser.CommentStream
83− , Ormolu.Parser.Pragma
84− , Ormolu.Parser.Result
85− , Ormolu.Printer
86− , Ormolu.Printer.Combinators
87− , Ormolu.Printer.Comments
88− , Ormolu.Printer.Internal
89− , Ormolu.Printer.Meat.Common
90− , Ormolu.Printer.Meat.Declaration
91− , Ormolu.Printer.Meat.Declaration.Annotation
92− , Ormolu.Printer.Meat.Declaration.Class
93− , Ormolu.Printer.Meat.Declaration.Data
94− , Ormolu.Printer.Meat.Declaration.Default
95− , Ormolu.Printer.Meat.Declaration.Foreign
96− , Ormolu.Printer.Meat.Declaration.Instance
97− , Ormolu.Printer.Meat.Declaration.RoleAnnotation
98− , Ormolu.Printer.Meat.Declaration.Rule
99− , Ormolu.Printer.Meat.Declaration.Signature
100− , Ormolu.Printer.Meat.Declaration.Splice
101− , Ormolu.Printer.Meat.Declaration.Type
102− , Ormolu.Printer.Meat.Declaration.TypeFamily
103− , Ormolu.Printer.Meat.Declaration.Value
104− , Ormolu.Printer.Meat.Declaration.Warning
105− , Ormolu.Printer.Meat.ImportExport
106− , Ormolu.Printer.Meat.Module
107− , Ormolu.Printer.Meat.Pragma
108− , Ormolu.Printer.Meat.Type
109− , Ormolu.Printer.Operators
110− , Ormolu.Printer.SpanStream
111− , Ormolu.Utils
112− other-modules: GHC
113− , GHC.DynFlags
114− if flag(dev)
115− ghc-options: -Wall -Werror -Wcompat
116− -Wincomplete-record-updates
117− -Wincomplete-uni-patterns
118− -Wnoncanonical-monad-instances
119− -Wnoncanonical-monadfail-instances
120− else
121− ghc-options: -O2 -Wall
122− default-language: Haskell2010
68+ exposed-modules:
69+ Ormolu
70+ Ormolu.Config
71+ Ormolu.Diff
72+ Ormolu.Exception
73+ Ormolu.Imports
74+ Ormolu.Parser
75+ Ormolu.Parser.Anns
76+ Ormolu.Parser.CommentStream
77+ Ormolu.Parser.Pragma
78+ Ormolu.Parser.Result
79+ Ormolu.Parser.Shebang
80+ Ormolu.Printer
81+ Ormolu.Printer.Combinators
82+ Ormolu.Printer.Comments
83+ Ormolu.Printer.Internal
84+ Ormolu.Printer.Meat.Common
85+ Ormolu.Printer.Meat.Declaration
86+ Ormolu.Printer.Meat.Declaration.Annotation
87+ Ormolu.Printer.Meat.Declaration.Class
88+ Ormolu.Printer.Meat.Declaration.Data
89+ Ormolu.Printer.Meat.Declaration.Default
90+ Ormolu.Printer.Meat.Declaration.Foreign
91+ Ormolu.Printer.Meat.Declaration.Instance
92+ Ormolu.Printer.Meat.Declaration.RoleAnnotation
93+ Ormolu.Printer.Meat.Declaration.Rule
94+ Ormolu.Printer.Meat.Declaration.Signature
95+ Ormolu.Printer.Meat.Declaration.Splice
96+ Ormolu.Printer.Meat.Declaration.Type
97+ Ormolu.Printer.Meat.Declaration.TypeFamily
98+ Ormolu.Printer.Meat.Declaration.Value
99+ Ormolu.Printer.Meat.Declaration.Warning
100+ Ormolu.Printer.Meat.ImportExport
101+ Ormolu.Printer.Meat.Module
102+ Ormolu.Printer.Meat.Pragma
103+ Ormolu.Printer.Meat.Type
104+ Ormolu.Printer.Operators
105+ Ormolu.Printer.SpanStream
106+ Ormolu.Processing.Common
107+ Ormolu.Processing.Cpp
108+ Ormolu.Processing.Postprocess
109+ Ormolu.Processing.Preprocess
110+ Ormolu.Utils
123111
124−test-suite tests
125− main-is: Spec.hs
126− hs-source-dirs: tests
127− type: exitcode-stdio-1.0
128− build-depends: base >= 4.12 && < 5.0
129− , containers >= 0.5 && < 0.7
130− , filepath >= 1.2 && < 1.5
131− , hspec >= 2.0 && < 3.0
132− , ormolu
133− , path >= 0.6 && < 0.7
134− , path-io >= 1.4.2 && < 2.0
135− , text >= 0.2 && < 1.3
136− build-tools: hspec-discover >= 2.0 && < 3.0
137− other-modules:
138− Ormolu.Parser.PragmaSpec
139− , Ormolu.PrinterSpec
112+ hs-source-dirs: src
113+ other-modules:
114+ GHC
115+ GHC.DynFlags
116+
117+ default-language: Haskell2010
118+ build-depends:
119+ base >=4.12 && <5.0,
120+ bytestring >=0.2 && <0.11,
121+ containers >=0.5 && <0.7,
122+ dlist >=0.8 && <2.0,
123+ exceptions >=0.6 && <0.11,
124+ ghc-lib-parser >=8.10 && <8.11,
125+ mtl >=2.0 && <3.0,
126+ syb >=0.7 && <0.8,
127+ text >=0.2 && <1.3
140128
141− if flag(dev)
142− ghc-options: -Wall -Werror
143− else
144− ghc-options: -O2 -Wall
145− default-language: Haskell2010
129+ if flag(dev)
130+ ghc-options:
131+ -Wall -Werror -Wcompat -Wincomplete-record-updates
132+ -Wincomplete-uni-patterns -Wnoncanonical-monad-instances
133+ -Wno-missing-home-modules
134+
135+ else
136+ ghc-options: -O2 -Wall
146137
147138executable ormolu
148− main-is: Main.hs
149− hs-source-dirs: app
150− build-depends: base >= 4.12 && < 5.0
151− , ghc-lib-parser == 8.8.1
152− , gitrev >= 1.3 && < 1.4
153− , optparse-applicative >= 0.14 && < 0.15
154− , ormolu
155− , text >= 0.2 && < 1.3
156− other-modules: Paths_ormolu
157− if flag(dev)
158− ghc-options: -Wall -Werror -Wcompat
159− -Wincomplete-record-updates
160− -Wincomplete-uni-patterns
161− -Wnoncanonical-monad-instances
162− -Wnoncanonical-monadfail-instances
163− else
164− ghc-options: -O2 -Wall
165− default-language: Haskell2010
139+ main-is: Main.hs
140+ hs-source-dirs: app
141+ other-modules: Paths_ormolu
142+ default-language: Haskell2010
143+ build-depends:
144+ base >=4.12 && <5.0,
145+ ghc-lib-parser >=8.10 && <8.11,
146+ gitrev >=1.3 && <1.4,
147+ optparse-applicative >=0.14 && <0.17,
148+ ormolu -any,
149+ text >=0.2 && <1.3
150+
151+ if flag(dev)
152+ ghc-options:
153+ -Wall -Werror -Wcompat -Wincomplete-record-updates
154+ -Wincomplete-uni-patterns -Wnoncanonical-monad-instances
155+ -optP-Wno-nonportable-include-path
156+
157+ else
158+ ghc-options: -O2 -Wall -rtsopts
159+
160+test-suite tests
161+ type: exitcode-stdio-1.0
162+ main-is: Spec.hs
163+ build-tools: hspec-discover >=2.0 && <3.0
164+ hs-source-dirs: tests
165+ other-modules:
166+ Ormolu.Parser.PragmaSpec
167+ Ormolu.PrinterSpec
168+
169+ default-language: Haskell2010
170+ build-depends:
171+ base >=4.12 && <5.0,
172+ containers >=0.5 && <0.7,
173+ filepath >=1.2 && <1.5,
174+ hspec >=2.0 && <3.0,
175+ ormolu -any,
176+ path >=0.6 && <0.8,
177+ path-io >=1.4.2 && <2.0,
178+ text >=0.2 && <1.3
179+
180+ if flag(dev)
181+ ghc-options: -Wall -Werror
182+
183+ else
184+ ghc-options: -O2 -Wall
region-tests/expected-result-17-18.hsnew file+17
@@ -0,0 +1,17 @@
1+{-# LANGUAGE LambdaCase #-}
2+
3+module Foo (
4+ foo, bar, baz) where
5+
6+foo :: Int
7+foo = 5
8+
9+bar :: Int -> Int
10+bar = \case
11+ 0 -> foo
12+ x -> x - foo
13+
14+baz :: Int -> Int
15+baz = gege
16+ where
17+ gege = 1 + 2
region-tests/expected-result-6-7.hsnew file+17
@@ -0,0 +1,17 @@
1+{-# LANGUAGE LambdaCase #-}
2+
3+module Foo (
4+ foo, bar, baz) where
5+
6+foo :: Int
7+foo = 5
8+
9+bar :: Int -> Int
10+bar = \case
11+ 0 -> foo
12+ x -> x - foo
13+
14+baz :: Int -> Int
15+baz = gege
16+ where
17+ gege = 1 + 2
region-tests/expected-result-6-8.hsnew file+16
@@ -0,0 +1,16 @@
1+{-# LANGUAGE LambdaCase #-}
2+
3+module Foo (
4+ foo, bar, baz) where
5+
6+foo :: Int
7+foo = 5
8+bar :: Int -> Int
9+bar = \case
10+ 0 -> foo
11+ x -> x - foo
12+
13+baz :: Int -> Int
14+baz = gege
15+ where
16+ gege = 1 + 2
region-tests/expected-result-9-12.hsnew file+17
@@ -0,0 +1,17 @@
1+{-# LANGUAGE LambdaCase #-}
2+
3+module Foo (
4+ foo, bar, baz) where
5+
6+foo :: Int
7+foo = 5
8+
9+bar :: Int -> Int
10+bar = \case
11+ 0 -> foo
12+ x -> x - foo
13+
14+baz :: Int -> Int
15+baz = gege
16+ where
17+ gege = 1 + 2
region-tests/expected-result-all.hsnew file+21
@@ -0,0 +1,21 @@
1+{-# LANGUAGE LambdaCase #-}
2+
3+module Foo
4+ ( foo,
5+ bar,
6+ baz,
7+ )
8+where
9+
10+foo :: Int
11+foo = 5
12+
13+bar :: Int -> Int
14+bar = \case
15+ 0 -> foo
16+ x -> x - foo
17+
18+baz :: Int -> Int
19+baz = gege
20+ where
21+ gege = 1 + 2
region-tests/src.hsnew file+17
@@ -0,0 +1,17 @@
1+{-# LANGUAGE LambdaCase #-}
2+
3+module Foo (
4+ foo, bar, baz) where
5+
6+foo :: Int
7+foo = 5
8+
9+bar :: Int -> Int
10+bar = \case
11+ 0 -> foo
12+ x -> x - foo
13+
14+baz :: Int -> Int
15+baz = gege
16+ where
17+ gege = 1 + 2
shell.nix+4−2
@@ -1,3 +1,5 @@
1−{ pkgs ? (import ./nix/nixpkgs) }:
1+{ pkgs ? (import ./nix/nixpkgs { inherit system; })
2+, system ? builtins.currentSystem
3+}:
24
3−(import ./default.nix { inherit pkgs; }).ormoluShell
5+(import ./default.nix { inherit pkgs; }).dev.ormoluShell
src/GHC.hs+2−13
@@ -1,23 +1,12 @@
11module GHC
22 ( module X,
3− ParsedSource,
43 )
54where
65
76import ApiAnnotation as X
87import BasicTypes as X
9−import HsBinds as X
10−import HsDecls as X
11−import HsDoc as X
12−import HsExpr as X
13−import HsExtension as X
14−import HsImpExp as X
15−import HsInstances as X ()
16−import HsLit as X
17−import HsPat as X
18−import HsSyn as X
8+import GHC.Hs as X
9+import GHC.Hs.Instances as X ()
1910import Module as X
2011import RdrName as X
2112import SrcLoc as X
22−
23−type ParsedSource = Located (HsModule GhcPs)
src/GHC/DynFlags.hs+33−20
@@ -1,5 +1,7 @@
11{-# OPTIONS_GHC -Wno-missing-fields #-}
22
3+-- Modified from ghc-lib-api-ext.
4+
35module GHC.DynFlags
46 ( baseDynFlags,
57 )
@@ -8,29 +10,40 @@ where
810import Config
911import DynFlags
1012import Fingerprint
11−import Platform
13+import GHC.Platform
14+import ToolSettings
1215
13−-- | Taken from HLint.
1416fakeSettings :: Settings
15−fakeSettings = Settings
16− { sTargetPlatform = platform,
17− sPlatformConstants = platformConstants,
18− sProjectVersion = cProjectVersion,
19− sProgramName = "ghc",
20− sOpt_P_fingerprint = fingerprint0
21− }
22− where
23− platform =
24− Platform
25− { platformWordSize = 8,
26− platformOS = OSUnknown,
27− platformUnregisterised = True
28− }
29− platformConstants =
30− PlatformConstants {pc_DYNAMIC_BY_DEFAULT = False, pc_WORD_SIZE = 8}
17+fakeSettings =
18+ Settings
19+ { sGhcNameVersion =
20+ GhcNameVersion
21+ { ghcNameVersion_programName = "ghc",
22+ ghcNameVersion_projectVersion = cProjectVersion
23+ },
24+ sFileSettings = FileSettings {},
25+ sTargetPlatform =
26+ Platform
27+ { platformWordSize = PW8,
28+ platformMini =
29+ PlatformMini
30+ { platformMini_arch = ArchUnknown,
31+ platformMini_os = OSUnknown
32+ },
33+ platformUnregisterised = True
34+ },
35+ sPlatformMisc = PlatformMisc {},
36+ sPlatformConstants =
37+ PlatformConstants {pc_DYNAMIC_BY_DEFAULT = False, pc_WORD_SIZE = 8},
38+ sToolSettings =
39+ ToolSettings
40+ { toolSettings_opt_P_fingerprint = fingerprint0,
41+ toolSettings_pgm_F = ""
42+ }
43+ }
3144
32−fakeLlvmConfig :: (LlvmTargets, LlvmPasses)
33−fakeLlvmConfig = ([], [])
45+fakeLlvmConfig :: LlvmConfig
46+fakeLlvmConfig = LlvmConfig [] []
3447
3548baseDynFlags :: DynFlags
3649baseDynFlags = defaultDynFlags fakeSettings fakeLlvmConfig
src/Ormolu.hs+19−19
@@ -1,5 +1,4 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE RecordWildCards #-}
32
43-- | A formatter for Haskell source code.
54module Ormolu
@@ -7,6 +6,7 @@ module Ormolu
76 ormoluFile,
87 ormoluStdin,
98 Config (..),
9+ RegionIndices (..),
1010 defaultConfig,
1111 DynOption (..),
1212 OrmoluException (..),
@@ -29,7 +29,6 @@ import Ormolu.Parser.Result
2929import Ormolu.Printer
3030import Ormolu.Utils (showOutputable)
3131import qualified SrcLoc as GHC
32−import System.IO (hGetContents, stdin)
3332
3433-- | Format a 'String', return formatted version as 'Text'.
3534--
@@ -44,26 +43,27 @@ import System.IO (hGetContents, stdin)
4443ormolu ::
4544 MonadIO m =>
4645 -- | Ormolu configuration
47− Config ->
46+ Config RegionIndices ->
4847 -- | Location of source file
4948 FilePath ->
5049 -- | Input to format
5150 String ->
5251 m Text
53−ormolu cfg path str = do
54− (ws, result0) <-
52+ormolu cfgWithIndices path str = do
53+ let totalLines = length (lines str)
54+ cfg = regionIndicesToDeltas totalLines <$> cfgWithIndices
55+ (warnings, result0) <-
5556 parseModule' cfg OrmoluParsingFailed path str
5657 when (cfgDebug cfg) $ do
5758 traceM "warnings:\n"
58− traceM (concatMap showWarn ws)
59+ traceM (concatMap showWarn warnings)
5960 traceM (prettyPrintParseResult result0)
60− -- NOTE We're forcing 'txt' here because otherwise errors (such as
61− -- messages about not-yet-supported functionality) will be thrown later
62− -- when we try to parse the rendered code back, inside of GHC monad
63− -- wrapper which will lead to error messages presenting the exceptions as
64− -- GHC bugs.
61+ -- We're forcing 'txt' here because otherwise errors (such as messages
62+ -- about not-yet-supported functionality) will be thrown later when we try
63+ -- to parse the rendered code back, inside of GHC monad wrapper which will
64+ -- lead to error messages presenting the exceptions as GHC bugs.
6565 let !txt = printModule result0
66− when (not (cfgUnsafe cfg) || cfgCheckIdempotency cfg) $ do
66+ when (not (cfgUnsafe cfg) || cfgCheckIdempotence cfg) $ do
6767 let pathRendered = path ++ "<rendered>"
6868 -- Parse the result of pretty-printing again and make sure that AST
6969 -- is the same as AST of original snippet module span positions.
@@ -79,7 +79,7 @@ ormolu cfg path str = do
7979 Different ss -> liftIO $ throwIO (OrmoluASTDiffers path ss)
8080 -- Try re-formatting the formatted result to check if we get exactly
8181 -- the same output.
82− when (cfgCheckIdempotency cfg) $
82+ when (cfgCheckIdempotence cfg) $
8383 let txt2 = printModule result1
8484 in case diffText txt txt2 pathRendered of
8585 Nothing -> return ()
@@ -96,7 +96,7 @@ ormolu cfg path str = do
9696ormoluFile ::
9797 MonadIO m =>
9898 -- | Ormolu configuration
99− Config ->
99+ Config RegionIndices ->
100100 -- | Location of source file
101101 FilePath ->
102102 -- | Resulting rendition
@@ -111,11 +111,11 @@ ormoluFile cfg path =
111111ormoluStdin ::
112112 MonadIO m =>
113113 -- | Ormolu configuration
114− Config ->
114+ Config RegionIndices ->
115115 -- | Resulting rendition
116116 m Text
117117ormoluStdin cfg =
118− liftIO (hGetContents stdin) >>= ormolu cfg "<stdin>"
118+ liftIO getContents >>= ormolu cfg "<stdin>"
119119
120120----------------------------------------------------------------------------
121121-- Helpers
@@ -124,7 +124,7 @@ ormoluStdin cfg =
124124parseModule' ::
125125 MonadIO m =>
126126 -- | Ormolu configuration
127− Config ->
127+ Config RegionDeltas ->
128128 -- | How to obtain 'OrmoluException' to throw when parsing fails
129129 (GHC.SrcSpan -> String -> OrmoluException) ->
130130 -- | File name to use in errors
@@ -133,10 +133,10 @@ parseModule' ::
133133 String ->
134134 m ([GHC.Warn], ParseResult)
135135parseModule' cfg mkException path str = do
136− (ws, r) <- parseModule cfg path str
136+ (warnings, r) <- parseModule cfg path str
137137 case r of
138138 Left (spn, err) -> liftIO $ throwIO (mkException spn err)
139− Right x -> return (ws, x)
139+ Right x -> return (warnings, x)
140140
141141-- | Pretty-print a 'GHC.Warn'.
142142showWarn :: GHC.Warn -> String
src/Ormolu/Config.hs+65−29
@@ -1,11 +1,13 @@
1−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2−{-# LANGUAGE OverloadedStrings #-}
1+{-# LANGUAGE DeriveFunctor #-}
32{-# LANGUAGE RecordWildCards #-}
43
54-- | Configuration options used by the tool.
65module Ormolu.Config
76 ( Config (..),
7+ RegionIndices (..),
8+ RegionDeltas (..),
89 defaultConfig,
10+ regionIndicesToDeltas,
911 DynOption (..),
1012 dynOptionToLocatedStr,
1113 )
@@ -14,38 +16,72 @@ where
1416import qualified SrcLoc as GHC
1517
1618-- | Ormolu configuration.
17−data Config
18− = Config
19− { -- | Dynamic options to pass to GHC parser
20− cfgDynOptions :: ![DynOption],
21− -- | Do formatting faster but without automatic detection of defects
22− cfgUnsafe :: !Bool,
23− -- | Output information useful for debugging
24− cfgDebug :: !Bool,
25− -- | Do not fail if CPP pragma is present (still doesn't handle CPP but
26− -- useful for formatting of files that enable the extension without
27− -- actually containing CPP macros)
28− cfgTolerateCpp :: !Bool,
29− -- | Checks if re-formatting the result is idempotent.
30− cfgCheckIdempotency :: !Bool
31− }
19+data Config region = Config
20+ { -- | Dynamic options to pass to GHC parser
21+ cfgDynOptions :: ![DynOption],
22+ -- | Do formatting faster but without automatic detection of defects
23+ cfgUnsafe :: !Bool,
24+ -- | Output information useful for debugging
25+ cfgDebug :: !Bool,
26+ -- | Checks if re-formatting the result is idempotent
27+ cfgCheckIdempotence :: !Bool,
28+ -- | Region selection
29+ cfgRegion :: !region
30+ }
31+ deriving (Eq, Show, Functor)
32+
33+-- | Region selection as the combination of start and end line numbers.
34+data RegionIndices = RegionIndices
35+ { -- | Start line of the region to format
36+ regionStartLine :: !(Maybe Int),
37+ -- | End line of the region to format
38+ regionEndLine :: !(Maybe Int)
39+ }
3240 deriving (Eq, Show)
3341
34−-- | Default 'Config'.
35−defaultConfig :: Config
36−defaultConfig = Config
37− { cfgDynOptions = [],
38− cfgUnsafe = False,
39− cfgDebug = False,
40− cfgTolerateCpp = False,
41− cfgCheckIdempotency = False
42+-- | Region selection as the length of the literal prefix and the literal
43+-- suffix.
44+data RegionDeltas = RegionDeltas
45+ { -- | Prefix length in number of lines
46+ regionPrefixLength :: !Int,
47+ -- | Suffix length in number of lines
48+ regionSuffixLength :: !Int
4249 }
50+ deriving (Eq, Show)
51+
52+-- | Default @'Config' 'RegionIndices'@.
53+defaultConfig :: Config RegionIndices
54+defaultConfig =
55+ Config
56+ { cfgDynOptions = [],
57+ cfgUnsafe = False,
58+ cfgDebug = False,
59+ cfgCheckIdempotence = False,
60+ cfgRegion =
61+ RegionIndices
62+ { regionStartLine = Nothing,
63+ regionEndLine = Nothing
64+ }
65+ }
66+
67+-- | Convert 'RegionIndices' into 'RegionDeltas'.
68+regionIndicesToDeltas ::
69+ -- | Total number of lines in the input
70+ Int ->
71+ -- | Region indices
72+ RegionIndices ->
73+ -- | Region deltas
74+ RegionDeltas
75+regionIndicesToDeltas total RegionIndices {..} =
76+ RegionDeltas
77+ { regionPrefixLength = maybe 0 (subtract 1) regionStartLine,
78+ regionSuffixLength = maybe 0 (total -) regionEndLine
79+ }
4380
4481-- | A wrapper for dynamic options.
45−newtype DynOption
46− = DynOption
47− { unDynOption :: String
48− }
82+newtype DynOption = DynOption
83+ { unDynOption :: String
84+ }
4985 deriving (Eq, Ord, Show)
5086
5187-- | Convert 'DynOption' to @'GHC.Located' 'String'@.
src/Ormolu/Diff.hs+25−17
@@ -1,6 +1,4 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
21{-# LANGUAGE RankNTypes #-}
3−{-# LANGUAGE RecordWildCards #-}
42
53-- | Diffing GHC ASTs modulo span positions.
64module Ormolu.Diff
@@ -10,14 +8,14 @@ module Ormolu.Diff
108 )
119where
1210
13−import BasicTypes (SourceText)
1411import Data.ByteString (ByteString)
1512import Data.Generics
1613import Data.Text (Text)
1714import qualified Data.Text as T
1815import qualified FastString as GHC
1916import GHC
20−import Ormolu.Imports (sortImports)
17+import Ormolu.Imports (normalizeImports)
18+import Ormolu.Parser.CommentStream
2119import Ormolu.Parser.Result
2220import Ormolu.Utils
2321
@@ -41,14 +39,16 @@ diffParseResult :: ParseResult -> ParseResult -> Diff
4139diffParseResult
4240 ParseResult
4341 { prCommentStream = cstream0,
44− prParsedSource = ps0
42+ prParsedSource = hs0
4543 }
4644 ParseResult
4745 { prCommentStream = cstream1,
48− prParsedSource = ps1
46+ prParsedSource = hs1
4947 } =
5048 matchIgnoringSrcSpans cstream0 cstream1
51− <> matchIgnoringSrcSpans ps0 ps1
49+ <> matchIgnoringSrcSpans
50+ hs0 {hsmodImports = normalizeImports (hsmodImports hs0)}
51+ hs1 {hsmodImports = normalizeImports (hsmodImports hs1)}
5252
5353-- | Compare two values for equality disregarding differences in 'SrcSpan's
5454-- and the ordering of import lists.
@@ -57,7 +57,7 @@ matchIgnoringSrcSpans = genericQuery
5757 where
5858 genericQuery :: GenericQ (GenericQ Diff)
5959 genericQuery x y
60− -- NOTE 'ByteString' implement 'Data' instance manually and does not
60+ -- 'ByteString' implements 'Data' instance manually and does not
6161 -- implement 'toConstr', so we have to deal with it in a special way.
6262 | Just x' <- cast x,
6363 Just y' <- cast y =
@@ -70,9 +70,10 @@ matchIgnoringSrcSpans = genericQuery
7070 gzipWithQ
7171 ( genericQuery
7272 `extQ` srcSpanEq
73− `extQ` hsModuleEq
73+ `extQ` commentEq
7474 `extQ` sourceTextEq
7575 `extQ` hsDocStringEq
76+ `extQ` importDeclQualifiedStyleEq
7677 `ext2Q` forLocated
7778 )
7879 x
@@ -80,16 +81,23 @@ matchIgnoringSrcSpans = genericQuery
8081 | otherwise = Different []
8182 srcSpanEq :: SrcSpan -> GenericQ Diff
8283 srcSpanEq _ _ = Same
83− hsModuleEq :: HsModule GhcPs -> GenericQ Diff
84− hsModuleEq hs0 hs1' =
85− case cast hs1' :: Maybe (HsModule GhcPs) of
84+ commentEq :: Comment -> GenericQ Diff
85+ commentEq (Comment _ x) d =
86+ case cast d :: Maybe Comment of
8687 Nothing -> Different []
87− Just hs1 ->
88− matchIgnoringSrcSpans
89− hs0 {hsmodImports = sortImports (hsmodImports hs0)}
90− hs1 {hsmodImports = sortImports (hsmodImports hs1)}
88+ Just (Comment _ y) ->
89+ if x == y
90+ then Same
91+ else Different []
9192 sourceTextEq :: SourceText -> GenericQ Diff
9293 sourceTextEq _ _ = Same
94+ importDeclQualifiedStyleEq :: ImportDeclQualifiedStyle -> GenericQ Diff
95+ importDeclQualifiedStyleEq d0 d1' =
96+ case (d0, cast d1' :: Maybe ImportDeclQualifiedStyle) of
97+ (x, Just x') | x == x' -> Same
98+ (QualifiedPre, Just QualifiedPost) -> Same
99+ (QualifiedPost, Just QualifiedPre) -> Same
100+ _ -> Different []
93101 hsDocStringEq :: HsDocString -> GenericQ Diff
94102 hsDocStringEq str0 str1' =
95103 case cast str1' :: Maybe HsDocString of
@@ -107,7 +115,7 @@ matchIgnoringSrcSpans = genericQuery
107115 appendSpan :: SrcSpan -> Diff -> Diff
108116 appendSpan s (Different ss) | fresh && helpful = Different (s : ss)
109117 where
110− fresh = not $ any (flip isSubspanOf s) ss
118+ fresh = not $ any (`isSubspanOf` s) ss
111119 helpful = isGoodSrcSpan s
112120 appendSpan _ d = d
113121
src/Ormolu/Exception.hs+24−19
@@ -8,6 +8,8 @@ module Ormolu.Exception
88where
99
1010import Control.Exception
11+import Data.List.NonEmpty (NonEmpty (..))
12+import qualified Data.List.NonEmpty as NE
1113import Data.Text (Text)
1214import qualified GHC
1315import Ormolu.Utils (showOutputable)
@@ -17,9 +19,7 @@ import System.IO
1719
1820-- | Ormolu exception representing all cases when Ormolu can fail.
1921data OrmoluException
20− = -- | Ormolu does not work with source files that use CPP
21− OrmoluCppEnabled FilePath
22− | -- | Parsing of original source code failed
22+ = -- | Parsing of original source code failed
2323 OrmoluParsingFailed GHC.SrcSpan String
2424 | -- | Parsing of formatted source code failed
2525 OrmoluOutputParsingFailed GHC.SrcSpan String
@@ -27,17 +27,14 @@ data OrmoluException
2727 OrmoluASTDiffers FilePath [GHC.SrcSpan]
2828 | -- | Formatted source code is not idempotent
2929 OrmoluNonIdempotentOutput GHC.RealSrcLoc Text Text
30+ | -- | Some GHC options were not recognized
31+ OrmoluUnrecognizedOpts (NonEmpty String)
3032 deriving (Eq, Show)
3133
3234instance Exception OrmoluException where
3335 displayException = \case
34− OrmoluCppEnabled path ->
35− unlines
36− [ "CPP is not supported:",
37− withIndent path
38− ]
3936 OrmoluParsingFailed s e ->
40− showParsingErr "Parsing of source code failed:" s [e]
37+ showParsingErr "The GHC parser (in Haddock mode) failed:" s [e]
4138 OrmoluOutputParsingFailed s e ->
4239 showParsingErr "Parsing of formatted code failed:" s [e]
4340 ++ "Please, consider reporting the bug.\n"
@@ -45,10 +42,12 @@ instance Exception OrmoluException where
4542 unlines $
4643 [ "AST of input and AST of formatted code differ."
4744 ]
48− ++ ( fmap withIndent $ case fmap (\s -> "at " ++ showOutputable s) ss of
49− [] -> ["in " ++ path]
50− xs -> xs
51− )
45+ ++ fmap
46+ withIndent
47+ ( case fmap (\s -> "at " ++ showOutputable s) ss of
48+ [] -> ["in " ++ path]
49+ xs -> xs
50+ )
5251 ++ ["Please, consider reporting the bug."]
5352 OrmoluNonIdempotentOutput loc left right ->
5453 showParsingErr
@@ -56,6 +55,11 @@ instance Exception OrmoluException where
5655 loc
5756 ["before: " ++ show left, "after: " ++ show right]
5857 ++ "Please, consider reporting the bug.\n"
58+ OrmoluUnrecognizedOpts opts ->
59+ unlines
60+ [ "The following GHC options were not recognized:",
61+ (withIndent . unwords . NE.toList) opts
62+ ]
5963
6064-- | Inside this wrapper 'OrmoluException' will be caught and displayed
6165-- nicely using 'displayException'.
@@ -70,12 +74,13 @@ withPrettyOrmoluExceptions m = m `catch` h
7074 hPutStrLn stderr (displayException e)
7175 exitWith . ExitFailure $
7276 case e of
73− -- Error code 1 is for `error` or `notImplemented`
74− OrmoluCppEnabled _ -> 2
75− OrmoluParsingFailed _ _ -> 3
76− OrmoluOutputParsingFailed _ _ -> 4
77− OrmoluASTDiffers _ _ -> 5
78− OrmoluNonIdempotentOutput _ _ _ -> 6
77+ -- Error code 1 is for 'error' or 'notImplemented'
78+ -- 2 used to be for erroring out on CPP
79+ OrmoluParsingFailed {} -> 3
80+ OrmoluOutputParsingFailed {} -> 4
81+ OrmoluASTDiffers {} -> 5
82+ OrmoluNonIdempotentOutput {} -> 6
83+ OrmoluUnrecognizedOpts {} -> 7
7984
8085----------------------------------------------------------------------------
8186-- Helpers
src/Ormolu/Imports.hs+172−51
@@ -1,81 +1,202 @@
11{-# LANGUAGE LambdaCase #-}
22{-# LANGUAGE RankNTypes #-}
3+{-# LANGUAGE RecordWildCards #-}
34
45-- | Manipulations on import lists.
56module Ormolu.Imports
6− ( sortImports,
7+ ( normalizeImports,
78 )
89where
910
1011import Data.Bifunctor
12+import Data.Char (isAlphaNum)
1113import Data.Function (on)
12−import Data.Generics (gcompare)
13−import Data.List (sortBy)
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)
1418import GHC hiding (GhcPs, IE)
15−import HsExtension
16−import HsImpExp (IE (..))
17−import Ormolu.Utils (notImplemented)
19+import GHC.Hs.Extension
20+import GHC.Hs.ImpExp (IE (..))
21+import Ormolu.Utils (notImplemented, showOutputable)
1822
19−-- | Sort imports by module name. This also sorts explicit import lists for
20−-- each declaration.
21−sortImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
22−sortImports = sortBy compareIdecl . fmap (fmap sortImportLists)
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))
2330 where
24− sortImportLists :: ImportDecl GhcPs -> ImportDecl GhcPs
25− sortImportLists decl =
26− decl
27− { ideclHiding = second (fmap sortLies) <$> ideclHiding decl
28− }
31+ g (L l ImportDecl {..}) =
32+ L
33+ l
34+ ImportDecl
35+ { ideclHiding = second (fmap normalizeLies) <$> ideclHiding,
36+ ..
37+ }
38+ g _ = notImplemented "XImportDecl"
2939
30−-- | Compare two @'LImportDecl' 'GhcPs'@ things.
31−compareIdecl :: LImportDecl GhcPs -> LImportDecl GhcPs -> Ordering
32−compareIdecl (L _ m0) (L _ m1) =
33− case (isPrelude n0, isPrelude n1) of
34− (False, False) -> n0 `compare` n1
35− (True, False) -> GT
36− (False, True) -> LT
37− (True, True) -> m0 `gcompare` m1
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+ }
3891 where
39− n0 = unLoc (ideclName m0)
40− n1 = unLoc (ideclName m1)
41− isPrelude = (== "Prelude") . moduleNameString
92+ isPrelude = moduleNameString moduleName == "Prelude"
93+ moduleName = unLoc ideclName
94+importId _ = notImplemented "XImportDecl"
4295
43−-- | Sort located import or export.
44−sortLies :: [LIE GhcPs] -> [LIE GhcPs]
45−sortLies = sortBy (compareIE `on` unLoc) . fmap (fmap sortThings)
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
99+ 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
46151
47−-- | Sort imports\/exports inside of 'IEThingWith'.
48−sortThings :: IE GhcPs -> IE GhcPs
49−sortThings = \case
50− IEThingWith NoExt x w xs fl ->
51− IEThingWith NoExt x w (sortBy (compareIewn `on` unLoc) xs) fl
52− other -> other
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)
53156
54−-- | Compare two located imports or exports.
55−compareIE :: IE GhcPs -> IE GhcPs -> Ordering
56−compareIE = compareIewn `on` getIewn
157+instance Ord IEWrappedNameOrd where
158+ compare (IEWrappedNameOrd x) (IEWrappedNameOrd y) = compareIewn x y
57159
58160-- | Project @'IEWrappedName' 'RdrName'@ from @'IE' 'GhcPs'@.
59−getIewn :: IE GhcPs -> IEWrappedName RdrName
161+getIewn :: IE GhcPs -> IEWrappedNameOrd
60162getIewn = \case
61− IEVar NoExt x -> unLoc x
62− IEThingAbs NoExt x -> unLoc x
63− IEThingAll NoExt x -> unLoc x
64− IEThingWith NoExt x _ _ _ -> unLoc x
65− IEModuleContents NoExt _ -> notImplemented "IEModuleContents"
66− IEGroup NoExt _ _ -> notImplemented "IEGroup"
67− IEDoc NoExt _ -> notImplemented "IEDoc"
68− IEDocNamed NoExt _ -> notImplemented "IEDocNamed"
69− XIE NoExt -> notImplemented "XIE"
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)
167+ IEModuleContents NoExtField _ -> notImplemented "IEModuleContents"
168+ IEGroup NoExtField _ _ -> notImplemented "IEGroup"
169+ IEDoc NoExtField _ -> notImplemented "IEDoc"
170+ IEDocNamed NoExtField _ -> notImplemented "IEDocNamed"
171+ XIE x -> noExtCon x
172+
173+-- | Like 'compareIewn' for located wrapped names.
174+compareLIewn :: LIEWrappedName RdrName -> LIEWrappedName RdrName -> Ordering
175+compareLIewn = compareIewn `on` unLoc
70176
71177-- | Compare two @'IEWrapppedName' 'RdrName'@ things.
72178compareIewn :: IEWrappedName RdrName -> IEWrappedName RdrName -> Ordering
73−compareIewn (IEName x) (IEName y) = unLoc x `compare` unLoc y
179+compareIewn (IEName x) (IEName y) = unLoc x `compareRdrName` unLoc y
74180compareIewn (IEName _) (IEPattern _) = LT
75181compareIewn (IEName _) (IEType _) = LT
76182compareIewn (IEPattern _) (IEName _) = GT
77−compareIewn (IEPattern x) (IEPattern y) = unLoc x `compare` unLoc y
183+compareIewn (IEPattern x) (IEPattern y) = unLoc x `compareRdrName` unLoc y
78184compareIewn (IEPattern _) (IEType _) = LT
79185compareIewn (IEType _) (IEName _) = GT
80186compareIewn (IEType _) (IEPattern _) = GT
81−compareIewn (IEType x) (IEType y) = unLoc x `compare` unLoc y
187+compareIewn (IEType x) (IEType y) = unLoc x `compareRdrName` unLoc y
188+
189+compareRdrName :: RdrName -> RdrName -> Ordering
190+compareRdrName x y =
191+ case (getNameStr x, getNameStr y) of
192+ ([], []) -> EQ
193+ ((_ : _), []) -> GT
194+ ([], (_ : _)) -> LT
195+ ((x' : _), (y' : _)) ->
196+ case (isAlphaNum x', isAlphaNum y') of
197+ (False, False) -> x `compare` y
198+ (True, False) -> LT
199+ (False, True) -> GT
200+ (True, True) -> x `compare` y
201+ where
202+ getNameStr = showOutputable . rdrNameOcc
src/Ormolu/Parser.hs+99−68
@@ -1,6 +1,5 @@
11{-# LANGUAGE LambdaCase #-}
22{-# LANGUAGE RecordWildCards #-}
3−{-# LANGUAGE StandaloneDeriving #-}
43{-# OPTIONS_GHC -fno-warn-orphans #-}
54
65-- | Parser for Haskell source code.
@@ -10,14 +9,16 @@ module Ormolu.Parser
109 )
1110where
1211
12+import Bag (bagToList)
1313import qualified CmdLineParser as GHC
1414import Control.Exception
15−import Control.Monad
1615import Control.Monad.IO.Class
17−import Data.List ((\\), foldl', isPrefixOf)
18−import Data.Maybe (catMaybes)
19−import qualified DynFlags as GHC
16+import qualified Data.List as L
17+import qualified Data.List.NonEmpty as NE
18+import Data.Ord (Down (Down))
19+import qualified Data.Text as T
2020import DynFlags as GHC
21+import ErrUtils (Severity (..), errMsgSeverity, errMsgSpan)
2122import qualified FastString as GHC
2223import GHC hiding (IE, UnicodeSyntax)
2324import GHC.DynFlags (baseDynFlags)
@@ -30,7 +31,8 @@ import Ormolu.Exception
3031import Ormolu.Parser.Anns
3132import Ormolu.Parser.CommentStream
3233import Ormolu.Parser.Result
33−import qualified Outputable as GHC
34+import Ormolu.Processing.Preprocess (preprocess)
35+import Ormolu.Utils (incSpanLine, removeIndentation)
3436import qualified Panic as GHC
3537import qualified Parser as GHC
3638import qualified StringBuffer as GHC
@@ -39,7 +41,7 @@ import qualified StringBuffer as GHC
3941parseModule ::
4042 MonadIO m =>
4143 -- | Ormolu configuration
42− Config ->
44+ Config RegionDeltas ->
4345 -- | File name (only for source location annotations)
4446 FilePath ->
4547 -- | Input for parser
@@ -48,17 +50,20 @@ parseModule ::
4850 ( [GHC.Warn],
4951 Either (SrcSpan, String) ParseResult
5052 )
51−parseModule Config {..} path input' = liftIO $ do
52− let (input, extraComments) = stripLinePragmas path input'
53− -- NOTE It's important that 'setDefaultExts' is done before
53+parseModule Config {..} path rawInput = liftIO $ do
54+ let (literalPrefix, indentedInput, literalSuffix, extraComments) =
55+ preprocess path rawInput cfgRegion
56+ (input, indent) = removeIndentation indentedInput
57+ -- It's important that 'setDefaultExts' is done before
5458 -- 'parsePragmasIntoDynFlags', because otherwise we might enable an
5559 -- extension that was explicitly disabled in the file.
5660 let baseFlags =
5761 GHC.setGeneralFlag'
5862 GHC.Opt_Haddock
5963 (setDefaultExts baseDynFlags)
64+ extraOpts = dynOptionToLocatedStr <$> cfgDynOptions
6065 (warnings, dynFlags) <-
61− parsePragmasIntoDynFlags baseFlags path input' >>= \case
66+ parsePragmasIntoDynFlags baseFlags extraOpts path rawInput >>= \case
6267 Right res -> pure res
6368 Left err ->
6469 let loc =
@@ -66,21 +71,59 @@ parseModule Config {..} path input' = liftIO $ do
6671 (mkSrcLoc (GHC.mkFastString path) 1 1)
6772 (mkSrcLoc (GHC.mkFastString path) 1 1)
6873 in throwIO (OrmoluParsingFailed loc err)
69− when (GHC.xopt Cpp dynFlags && not cfgTolerateCpp) $
70− throwIO (OrmoluCppEnabled path)
71− let r = case runParser GHC.parseModule dynFlags path input of
72− GHC.PFailed _ ss m ->
73− Left (ss, GHC.showSDoc dynFlags m)
74− GHC.POk pstate pmod ->
75− let (comments, exts) = mkCommentStream extraComments pstate
76− in Right ParseResult
77− { prParsedSource = pmod,
78− prAnns = mkAnns pstate,
79− prCommentStream = comments,
80− prExtensions = exts
81− }
74+ let useRecordDot =
75+ "record-dot-preprocessor" == pgm_F dynFlags
76+ || any
77+ (("RecordDotPreprocessor" ==) . moduleNameString)
78+ (pluginModNames dynFlags)
79+ pStateErrors = \pstate ->
80+ let errs = bagToList $ GHC.getErrorMessages pstate dynFlags
81+ fixupErrSpan = incSpanLine (regionPrefixLength cfgRegion)
82+ in case L.sortOn (Down . SeverityOrd . errMsgSeverity) errs of
83+ [] -> Nothing
84+ err : _ ->
85+ -- Show instance returns a short error message
86+ Just (fixupErrSpan (errMsgSpan err), show err)
87+ r = case runParser GHC.parseModule dynFlags path input of
88+ GHC.PFailed pstate ->
89+ case pStateErrors pstate of
90+ Just err -> Left err
91+ Nothing -> error "PFailed does not have an error"
92+ GHC.POk pstate (L _ hsModule) ->
93+ case pStateErrors pstate of
94+ -- Some parse errors (pattern/arrow syntax in expr context)
95+ -- do not cause a parse error, but they are replaced with "_"
96+ -- by the parser and the modified AST is propagated to the
97+ -- later stages; but we fail in those cases.
98+ Just err -> Left err
99+ Nothing ->
100+ let (stackHeader, shebangs, pragmas, comments) =
101+ mkCommentStream input extraComments pstate
102+ in Right
103+ ParseResult
104+ { prParsedSource = hsModule,
105+ prAnns = mkAnns pstate,
106+ prStackHeader = stackHeader,
107+ prShebangs = shebangs,
108+ prPragmas = pragmas,
109+ prCommentStream = comments,
110+ prUseRecordDot = useRecordDot,
111+ prImportQualifiedPost =
112+ GHC.xopt ImportQualifiedPost dynFlags,
113+ prLiteralPrefix = T.pack literalPrefix,
114+ prLiteralSuffix = T.pack literalSuffix,
115+ prIndent = indent
116+ }
82117 return (warnings, r)
83118
119+-- | Enable all language extensions that we think should be enabled by
120+-- default for ease of use.
121+setDefaultExts :: DynFlags -> DynFlags
122+setDefaultExts flags = L.foldl' xopt_set flags autoExts
123+ where
124+ autoExts = allExts L.\\ manualExts
125+ allExts = [minBound .. maxBound]
126+
84127-- | Extensions that are not enabled automatically and should be activated
85128-- by user.
86129manualExts :: [Extension]
@@ -100,14 +143,13 @@ manualExts =
100143 MonadComprehensions,
101144 UnboxedSums,
102145 UnicodeSyntax, -- gives special meanings to operators like (→)
103− TemplateHaskellQuotes -- enables TH subset of quasi-quotes, this
146+ TemplateHaskellQuotes, -- enables TH subset of quasi-quotes, this
104147 -- apparently interferes with QuasiQuotes in
105148 -- weird ways
149+ ImportQualifiedPost -- affects how Ormolu renders imports, so the
150+ -- decision of enabling this style is left to the user
106151 ]
107152
108−----------------------------------------------------------------------------
109−-- Helpers (taken from ghc-exactprint)
110−
111153-- | Run a 'GHC.P' computation.
112154runParser ::
113155 -- | Computation to run
@@ -126,58 +168,47 @@ runParser parser flags filename input = GHC.unP parser parseState
126168 buffer = GHC.stringToStringBuffer input
127169 parseState = GHC.mkPState flags buffer location
128170
129−-- | Remove GHC style line pragams (@{-# LINE .. #-}@) and convert them into
130−-- comments.
131−stripLinePragmas :: FilePath -> String -> (String, [Located String])
132−stripLinePragmas path = unlines' . unzip . findLines path . lines
133− where
134− unlines' (a, b) = (unlines a, catMaybes b)
171+-- | Wrap GHC's 'Severity' to add 'Ord' instance.
172+newtype SeverityOrd = SeverityOrd Severity
135173
136−findLines :: FilePath -> [String] -> [(String, Maybe (Located String))]
137−findLines path = zipWith (checkLine path) [1 ..]
174+instance Eq SeverityOrd where
175+ s1 == s2 = compare s1 s2 == EQ
138176
139−checkLine :: FilePath -> Int -> String -> (String, Maybe (Located String))
140−checkLine path line s
141− | "{-# LINE" `isPrefixOf` s =
142− let (pragma, res) = getPragma s
143− size = length pragma
144− ss = mkSrcSpan (mkSrcLoc' 1) (mkSrcLoc' (size + 1))
145− in (res, Just $ L ss pragma)
146− | "#!" `isPrefixOf` s =
147− let ss = mkSrcSpan (mkSrcLoc' 1) (mkSrcLoc' (length s))
148− in ("", Just $ L ss s)
149− | otherwise = (s, Nothing)
150− where
151− mkSrcLoc' = mkSrcLoc (GHC.mkFastString path) line
152−
153−getPragma :: String -> (String, String)
154−getPragma [] = error "Ormolu.Parser.getPragma: input must not be empty"
155−getPragma s@(x : xs)
156− | "#-}" `isPrefixOf` s = ("#-}", " " ++ drop 3 s)
157− | otherwise =
158− let (prag, remline) = getPragma xs
159− in (x : prag, ' ' : remline)
160−
161−-- | Enable all language extensions that we think should be enabled by
162−-- default for ease of use.
163−setDefaultExts :: DynFlags -> DynFlags
164−setDefaultExts flags = foldl' GHC.xopt_set flags autoExts
165− where
166− autoExts = allExts \\ manualExts
167− allExts = [minBound .. maxBound]
177+instance Ord SeverityOrd where
178+ compare (SeverityOrd s1) (SeverityOrd s2) =
179+ compare (f s1) (f s2)
180+ where
181+ f :: Severity -> Int
182+ f SevOutput = 1
183+ f SevFatal = 2
184+ f SevInteractive = 3
185+ f SevDump = 4
186+ f SevInfo = 5
187+ f SevWarning = 6
188+ f SevError = 7
168189
169190----------------------------------------------------------------------------
170−-- More helpers (taken from HLint)
191+-- Helpers taken from HLint
171192
172193parsePragmasIntoDynFlags ::
194+ -- | Pre-set 'DynFlags'
173195 DynFlags ->
196+ -- | Extra options (provided by user)
197+ [Located String] ->
198+ -- | File name (only for source location annotations)
174199 FilePath ->
200+ -- | Input for parser
175201 String ->
176202 IO (Either String ([GHC.Warn], DynFlags))
177−parsePragmasIntoDynFlags flags filepath str =
203+parsePragmasIntoDynFlags flags extraOpts filepath str =
178204 catchErrors $ do
179205 let opts = GHC.getOptions flags (GHC.stringToStringBuffer str) filepath
180− (flags', _, warnings) <- parseDynamicFilePragma flags opts
206+ (flags', leftovers, warnings) <-
207+ parseDynamicFilePragma flags (opts <> extraOpts)
208+ case NE.nonEmpty leftovers of
209+ Nothing -> return ()
210+ Just unrecognizedOpts ->
211+ throwIO (OrmoluUnrecognizedOpts (unLoc <$> unrecognizedOpts))
181212 let flags'' = flags' `gopt_set` Opt_KeepRawTokenStream
182213 return $ Right (warnings, flags'')
183214 where
src/Ormolu/Parser/CommentStream.hs+152−64
@@ -1,79 +1,77 @@
1−{-# LANGUAGE BangPatterns #-}
21{-# LANGUAGE DeriveDataTypeable #-}
32{-# LANGUAGE GeneralizedNewtypeDeriving #-}
43{-# LANGUAGE LambdaCase #-}
5−{-# LANGUAGE TupleSections #-}
64
75-- | Functions for working with comment stream.
86module Ormolu.Parser.CommentStream
9− ( CommentStream (..),
10− Comment (..),
7+ ( -- * Comment stream
8+ CommentStream (..),
119 mkCommentStream,
12− isPrevHaddock,
13− isMultilineComment,
1410 showCommentStream,
11+
12+ -- * Comment
13+ Comment (..),
14+ unComment,
15+ hasAtomsBefore,
16+ isMultilineComment,
1517 )
1618where
1719
1820import Data.Char (isSpace)
1921import Data.Data (Data)
20−import Data.Either (partitionEithers)
21−import Data.List (dropWhileEnd, isPrefixOf, sortOn)
22+import qualified Data.List as L
2223import Data.List.NonEmpty (NonEmpty (..))
2324import qualified Data.List.NonEmpty as NE
2425import Data.Maybe (mapMaybe)
2526import qualified GHC
2627import qualified Lexer as GHC
2728import Ormolu.Parser.Pragma
28−import Ormolu.Utils (showOutputable)
29+import Ormolu.Parser.Shebang
30+import Ormolu.Processing.Common
31+import Ormolu.Utils (onTheSameLine, showOutputable)
2932import SrcLoc
3033
34+----------------------------------------------------------------------------
35+-- Comment stream
36+
3137-- | A stream of 'RealLocated' 'Comment's in ascending order with respect to
3238-- beginning of corresponding spans.
3339newtype CommentStream = CommentStream [RealLocated Comment]
3440 deriving (Eq, Data, Semigroup, Monoid)
3541
36−-- | A wrapper for a single comment. The 'NonEmpty' list inside contains
37−-- lines of multiline comment @{- … -}@ or just single item\/line otherwise.
38−newtype Comment = Comment (NonEmpty String)
39− deriving (Eq, Show, Data)
40−
41−-- | Create 'CommentStream' from 'GHC.PState'. We also create a 'Set' of
42−-- extensions here, which is not sorted in any way. The pragma comment are
43−-- removed from the 'CommentStream'.
42+-- | Create 'CommentStream' from 'GHC.PState'. The pragmas and shebangs are
43+-- removed from the 'CommentStream'. Shebangs are only extracted from the
44+-- comments that come from the first argument.
4445mkCommentStream ::
46+ -- | Original input
47+ String ->
4548 -- | Extra comments to include
4649 [Located String] ->
4750 -- | Parser state to use for comment extraction
4851 GHC.PState ->
49− -- | Comment stream and a set of extracted pragmas
50− (CommentStream, [Pragma])
51−mkCommentStream extraComments pstate =
52− ( CommentStream $
53− -- NOTE It's easier to normalize pragmas right when we construct comment
54− -- streams. Because this way we need to do it only once and when we
55− -- perform checking later they'll automatically match.
56− mkComment <$> sortOn (realSrcSpanStart . getRealSrcSpan) comments,
57− pragmas
52+ -- | Stack header, shebangs, pragmas, and comment stream
53+ ( Maybe (RealLocated Comment),
54+ [Shebang],
55+ [([RealLocated Comment], Pragma)],
56+ CommentStream
57+ )
58+mkCommentStream input extraComments pstate =
59+ ( mstackHeader,
60+ shebangs,
61+ pragmas,
62+ CommentStream comments
5863 )
5964 where
60− (comments, pragmas) = partitionEithers (partitionComments <$> rawComments)
61− rawComments =
62− mapMaybe toRealSpan $
63− extraComments
65+ (comments, pragmas) = extractPragmas input rawComments1
66+ (rawComments1, mstackHeader) = extractStackHeader rawComments0
67+ rawComments0 =
68+ L.sortOn (realSrcSpanStart . getRealSrcSpan) . mapMaybe toRealSpan $
69+ otherExtraComments
6470 ++ mapMaybe (liftMaybe . fmap unAnnotationComment) (GHC.comment_q pstate)
6571 ++ concatMap
6672 (mapMaybe (liftMaybe . fmap unAnnotationComment) . snd)
6773 (GHC.annotations_comments pstate)
68−
69−-- | Test whether a 'Comment' looks like a Haddock following a definition,
70−-- i.e. something starting with @-- ^@.
71−isPrevHaddock :: Comment -> Bool
72−isPrevHaddock (Comment (x :| _)) = "-- ^" `isPrefixOf` x
73−
74−-- | Is this comment multiline-style?
75−isMultilineComment :: Comment -> Bool
76−isMultilineComment (Comment (x :| _)) = "{-" `isPrefixOf` x
74+ (shebangs, otherExtraComments) = extractShebangs extraComments
7775
7876-- | Pretty-print a 'CommentStream'.
7977showCommentStream :: CommentStream -> String
@@ -84,28 +82,117 @@ showCommentStream (CommentStream xs) =
8482 showComment (GHC.L l str) = showOutputable l ++ " " ++ show str
8583
8684----------------------------------------------------------------------------
87−-- Helpers
85+-- Comment
86+
87+-- | A wrapper for a single comment. The 'Bool' indicates whether there were
88+-- atoms before beginning of the comment in the original input. The
89+-- 'NonEmpty' list inside contains lines of multiline comment @{- … -}@ or
90+-- just single item\/line otherwise.
91+data Comment = Comment Bool (NonEmpty String)
92+ deriving (Eq, Show, Data)
8893
8994-- | Normalize comment string. Sometimes one multi-line comment is turned
9095-- into several lines for subsequent outputting with correct indentation for
9196-- each line.
92−mkComment :: RealLocated String -> RealLocated Comment
93−mkComment (L l s) =
94− L l . Comment . fmap dropTrailing $
95− if "{-" `isPrefixOf` s
96− then case NE.nonEmpty (lines s) of
97− Nothing -> s :| []
98− Just (x :| xs) ->
99− let getIndent y =
100− if all isSpace y
101− then startIndent
102− else length (takeWhile isSpace y)
103− n = minimum (startIndent : fmap getIndent xs)
104− in x :| (drop n <$> xs)
105− else s :| []
97+mkComment ::
98+ -- | Lines of original input with their indices
99+ [(Int, String)] ->
100+ -- | Raw comment string
101+ RealLocated String ->
102+ -- | Remaining lines of original input and the constructed 'Comment'
103+ ([(Int, String)], RealLocated Comment)
104+mkComment ls (L l s) = (ls', comment)
106105 where
107− dropTrailing = dropWhileEnd isSpace
106+ comment =
107+ L l . Comment atomsBefore . removeConseqBlanks . fmap dropTrailing $
108+ if "{-" `L.isPrefixOf` s
109+ then case NE.nonEmpty (lines s) of
110+ Nothing -> s :| []
111+ Just (x :| xs) ->
112+ let getIndent y =
113+ if all isSpace y || y == endDisabling
114+ then startIndent
115+ else length (takeWhile isSpace y)
116+ n = minimum (startIndent : fmap getIndent xs)
117+ removeIndent y =
118+ if y == endDisabling
119+ then y
120+ else drop n y
121+ in x :| (removeIndent <$> xs)
122+ else s :| []
123+ (atomsBefore, ls') =
124+ case dropWhile ((< commentLine) . fst) ls of
125+ [] -> (False, [])
126+ ((_, i) : ls'') ->
127+ case take 2 (dropWhile isSpace i) of
128+ "--" -> (False, ls'')
129+ "{-" -> (False, ls'')
130+ _ -> (True, ls'')
131+ dropTrailing = L.dropWhileEnd isSpace
108132 startIndent = srcSpanStartCol l - 1
133+ commentLine = srcSpanStartLine l
134+
135+-- | Get a collection of lines from a 'Comment'.
136+unComment :: Comment -> NonEmpty String
137+unComment (Comment _ xs) = xs
138+
139+-- | Check whether the 'Comment' had some non-whitespace atoms in front of
140+-- it in the original input.
141+hasAtomsBefore :: Comment -> Bool
142+hasAtomsBefore (Comment atomsBefore _) = atomsBefore
143+
144+-- | Is this comment multiline-style?
145+isMultilineComment :: Comment -> Bool
146+isMultilineComment (Comment _ (x :| _)) = "{-" `L.isPrefixOf` x
147+
148+----------------------------------------------------------------------------
149+-- Helpers
150+
151+-- | Detect and extract stack header if it is present.
152+extractStackHeader ::
153+ -- | Comment stream to analyze
154+ [RealLocated String] ->
155+ ([RealLocated String], Maybe (RealLocated Comment))
156+extractStackHeader = \case
157+ [] -> ([], Nothing)
158+ (x : xs) ->
159+ let comment = snd (mkComment [] x)
160+ in if isStackHeader (unRealSrcSpan comment)
161+ then (xs, Just comment)
162+ else (x : xs, Nothing)
163+ where
164+ isStackHeader (Comment _ (x :| _)) =
165+ "stack" `L.isPrefixOf` dropWhile isSpace (drop 2 x)
166+
167+-- | Extract pragmas and their associated comments.
168+extractPragmas ::
169+ -- | Input
170+ String ->
171+ -- | Comment stream to analyze
172+ [RealLocated String] ->
173+ ([RealLocated Comment], [([RealLocated Comment], Pragma)])
174+extractPragmas input = go initialLs id id
175+ where
176+ initialLs = zip [1 ..] (lines input)
177+ go ls csSoFar pragmasSoFar = \case
178+ [] -> (csSoFar [], pragmasSoFar [])
179+ (x : xs) ->
180+ case parsePragma (unRealSrcSpan x) of
181+ Nothing ->
182+ let (ls', x') = mkComment ls x
183+ in go ls' (csSoFar . (x' :)) pragmasSoFar xs
184+ Just pragma ->
185+ let combined ys = (csSoFar ys, pragma)
186+ go' ls' ys rest = go ls' id (pragmasSoFar . (combined ys :)) rest
187+ in case xs of
188+ [] -> go' ls [] xs
189+ (y : ys) ->
190+ let (ls', y') = mkComment ls y
191+ in if onTheSameLine
192+ (RealSrcSpan (getRealSrcSpan x))
193+ (RealSrcSpan (getRealSrcSpan y))
194+ then go' ls' [y'] ys
195+ else go' ls [] xs
109196
110197-- | Get a 'String' from 'GHC.AnnotationComment'.
111198unAnnotationComment :: GHC.AnnotationComment -> Maybe String
@@ -127,12 +214,13 @@ toRealSpan :: Located a -> Maybe (RealLocated a)
127214toRealSpan (L (RealSrcSpan l) a) = Just (L l a)
128215toRealSpan _ = Nothing
129216
130−-- | If a given comment is a pragma, return it in parsed form in 'Right'.
131−-- Otherwise return the original comment unchanged.
132−partitionComments ::
133− RealLocated String ->
134− Either (RealLocated String) Pragma
135−partitionComments input =
136− case parsePragma (unRealSrcSpan input) of
137− Nothing -> Left input
138− Just pragma -> Right pragma
217+-- | Remove consecutive blank lines.
218+removeConseqBlanks :: NonEmpty String -> NonEmpty String
219+removeConseqBlanks (x :| xs) = x :| go (null x) id xs
220+ where
221+ go seenBlank acc = \case
222+ [] -> acc []
223+ (y : ys) ->
224+ if seenBlank && null y
225+ then go True acc ys
226+ else go (null y) (acc . (y :)) ys
src/Ormolu/Parser/Pragma.hs+5−5
@@ -10,7 +10,7 @@ where
1010
1111import Control.Monad
1212import Data.Char (isSpace, toLower)
13−import Data.List
13+import qualified Data.List as L
1414import qualified EnumSet as ES
1515import FastString (mkFastString, unpackFS)
1616import qualified Lexer as L
@@ -35,8 +35,8 @@ parsePragma ::
3535 String ->
3636 Maybe Pragma
3737parsePragma input = do
38− inputNoPrefix <- stripPrefix "{-#" input
39− guard ("#-}" `isSuffixOf` input)
38+ inputNoPrefix <- L.stripPrefix "{-#" input
39+ guard ("#-}" `L.isSuffixOf` input)
4040 let contents = take (length inputNoPrefix - 3) inputNoPrefix
4141 (pragmaName, cs) = (break isSpace . dropWhile isSpace) contents
4242 case toLower <$> pragmaName of
@@ -46,7 +46,7 @@ parsePragma input = do
4646 _ -> Nothing
4747 where
4848 trimSpaces :: String -> String
49− trimSpaces = dropWhileEnd isSpace . dropWhile isSpace
49+ trimSpaces = L.dropWhileEnd isSpace . dropWhile isSpace
5050
5151-- | Assuming the input consists of a series of tokens from a language
5252-- pragma, return the set of enabled extensions.
@@ -54,7 +54,7 @@ parseExtensions :: String -> Maybe [String]
5454parseExtensions str = tokenize str >>= go
5555 where
5656 go = \case
57− (L.ITconid ext : []) -> return [unpackFS ext]
57+ [L.ITconid ext] -> return [unpackFS ext]
5858 (L.ITconid ext : L.ITcomma : xs) -> (unpackFS ext :) <$> go xs
5959 _ -> Nothing
6060
src/Ormolu/Parser/Result.hs+26−11
@@ -7,23 +7,38 @@ module Ormolu.Parser.Result
77 )
88where
99
10+import Data.Text (Text)
1011import GHC
1112import Ormolu.Parser.Anns
1213import Ormolu.Parser.CommentStream
1314import Ormolu.Parser.Pragma (Pragma)
15+import Ormolu.Parser.Shebang (Shebang)
1416
1517-- | A collection of data that represents a parsed module in Ormolu.
16−data ParseResult
17− = ParseResult
18− { -- | 'ParsedSource' from GHC
19− prParsedSource :: ParsedSource,
20− -- | Ormolu-specfic representation of annotations
21− prAnns :: Anns,
22− -- | Comment stream
23− prCommentStream :: CommentStream,
24− -- | Extensions enabled in that module
25− prExtensions :: [Pragma]
26− }
18+data ParseResult = ParseResult
19+ { -- | 'ParsedSource' from GHC
20+ prParsedSource :: HsModule GhcPs,
21+ -- | Ormolu-specfic representation of annotations
22+ prAnns :: Anns,
23+ -- | Stack header
24+ prStackHeader :: Maybe (RealLocated Comment),
25+ -- | Shebangs found in the input
26+ prShebangs :: [Shebang],
27+ -- | Pragmas and the associated comments
28+ prPragmas :: [([RealLocated Comment], Pragma)],
29+ -- | Comment stream
30+ prCommentStream :: CommentStream,
31+ -- | Whether or not record dot syntax is enabled
32+ prUseRecordDot :: Bool,
33+ -- | Whether or not ImportQualifiedPost is enabled
34+ prImportQualifiedPost :: Bool,
35+ -- | Literal prefix
36+ prLiteralPrefix :: Text,
37+ -- | Literal suffix
38+ prLiteralSuffix :: Text,
39+ -- | Indentation level, can be non-zero in case of region formatting
40+ prIndent :: Int
41+ }
2742
2843-- | Pretty-print a 'ParseResult'.
2944prettyPrintParseResult :: ParseResult -> String
src/Ormolu/Parser/Shebang.hsnew file+27
@@ -0,0 +1,27 @@
1+{-# LANGUAGE DeriveDataTypeable #-}
2+
3+-- | A module for dealing with shebangs.
4+module Ormolu.Parser.Shebang
5+ ( Shebang (..),
6+ extractShebangs,
7+ isShebang,
8+ )
9+where
10+
11+import Data.Data (Data)
12+import qualified Data.List as L
13+import SrcLoc
14+
15+-- | A wrapper for a shebang.
16+newtype Shebang = Shebang (Located String)
17+ deriving (Eq, Data)
18+
19+-- | Extract shebangs from the beginning of a comment stream.
20+extractShebangs :: [Located String] -> ([Shebang], [Located String])
21+extractShebangs comments = (Shebang <$> shebangs, rest)
22+ where
23+ (shebangs, rest) = span (isShebang . unLoc) comments
24+
25+-- | Return 'True' if given 'String' is a shebang.
26+isShebang :: String -> Bool
27+isShebang str = "#!" `L.isPrefixOf` str
src/Ormolu/Printer.hs+17−7
@@ -1,5 +1,3 @@
1−{-# LANGUAGE LambdaCase #-}
2−{-# LANGUAGE OverloadedStrings #-}
31{-# LANGUAGE RecordWildCards #-}
42
53-- | Pretty-printer for Haskell AST.
@@ -13,6 +11,7 @@ import Ormolu.Parser.Result
1311import Ormolu.Printer.Combinators
1412import Ormolu.Printer.Meat.Module
1513import Ormolu.Printer.SpanStream
14+import Ormolu.Processing.Postprocess (postprocess)
1615
1716-- | Render a module.
1817printModule ::
@@ -21,8 +20,19 @@ printModule ::
2120 -- | Resulting rendition
2221 Text
2322printModule ParseResult {..} =
24− runR
25− (p_hsModule prExtensions prParsedSource)
26− (mkSpanStream prParsedSource)
27− prCommentStream
28− prAnns
23+ prLiteralPrefix <> region <> prLiteralSuffix
24+ where
25+ region =
26+ postprocess prIndent $
27+ runR
28+ ( p_hsModule
29+ prStackHeader
30+ prShebangs
31+ prPragmas
32+ prImportQualifiedPost
33+ prParsedSource
34+ )
35+ (mkSpanStream prParsedSource)
36+ prCommentStream
37+ prAnns
38+ prUseRecordDot
src/Ormolu/Printer/Combinators.hs+39−54
@@ -2,7 +2,6 @@
22{-# LANGUAGE GADTs #-}
33{-# LANGUAGE LambdaCase #-}
44{-# LANGUAGE OverloadedStrings #-}
5−{-# LANGUAGE RecordWildCards #-}
65
76-- | Printing combinators. The definitions here are presented in such an
87-- order so you can just go through the Haddocks and by the end of the file
@@ -22,9 +21,9 @@ module Ormolu.Printer.Combinators
2221 space,
2322 newline,
2423 inci,
24+ inciIf,
2525 located,
2626 located',
27− locatedPat,
2827 switchLayout,
2928 Layout (..),
3029 vlayout,
@@ -53,58 +52,57 @@ module Ormolu.Printer.Combinators
5352
5453 -- ** Literals
5554 comma,
55+ commaDel,
56+ equals,
5657
57− -- ** Comments
58+ -- ** Stateful markers
59+ SpanMark (..),
60+ spanMarkSpan,
5861 HaddockStyle (..),
59− setLastCommentSpan,
60− getLastCommentSpan,
62+ setSpanMark,
63+ getSpanMark,
6164 )
6265where
6366
6467import Control.Monad
65−import Data.Data (Data)
6668import Data.List (intersperse)
6769import Data.Text (Text)
68−import GHC (Pat (XPat), XXPat)
6970import Ormolu.Printer.Comments
7071import Ormolu.Printer.Internal
71−import Ormolu.Utils (isModule)
7272import SrcLoc
7373
7474----------------------------------------------------------------------------
7575-- Basic
7676
77+-- | Indent the inner expression if the first argument is 'True'.
78+inciIf ::
79+ -- | Whether to indent
80+ Bool ->
81+ -- | The expression to indent
82+ R () ->
83+ R ()
84+inciIf b m = if b then inci m else m
85+
7786-- | Enter a 'Located' entity. This combinator handles outputting comments
7887-- and sets layout (single-line vs multi-line) for the inner computation.
7988-- Roughly, the rule for using 'located' is that every time there is a
8089-- 'Located' wrapper, it should be “discharged” with a corresponding
8190-- 'located' invocation.
8291located ::
83− Data a =>
8492 -- | Thing to enter
8593 Located a ->
8694 -- | How to render inner value
8795 (a -> R ()) ->
8896 R ()
89−located loc f = do
90− let withRealLocated (L l a) g =
91− case l of
92− UnhelpfulSpan _ -> return ()
93− RealSrcSpan l' -> g (L l' a)
94− withRealLocated loc spitPrecedingComments
95− let setEnclosingSpan =
96− case getLoc loc of
97− UnhelpfulSpan _ -> id
98− RealSrcSpan orf ->
99− if isModule (unLoc loc)
100− then id
101− else withEnclosingSpan orf
102− setEnclosingSpan $ switchLayout [getLoc loc] (f (unLoc loc))
103− withRealLocated loc spitFollowingComments
97+located (L (UnhelpfulSpan _) a) f = f a
98+located (L (RealSrcSpan l) a) f = do
99+ spitPrecedingComments l
100+ withEnclosingSpan l $
101+ switchLayout [RealSrcSpan l] (f a)
102+ spitFollowingComments l
104103
105104-- | A version of 'located' with arguments flipped.
106105located' ::
107− Data a =>
108106 -- | How to render inner value
109107 (a -> R ()) ->
110108 -- | Thing to enter
@@ -112,28 +110,6 @@ located' ::
112110 R ()
113111located' = flip located
114112
115−-- | A version of 'located' that works on 'Pat'.
116−--
117−-- Starting from GHC 8.8, @'LPat' == 'Pat'@. Located 'Pat's are always
118−-- constructed with the 'XPat' constructor, containing a @'Located' 'Pat'@.
119−--
120−-- Most of the time, we can just use 'p_pat' directly, because it handles
121−-- located 'Pat's. However, sometimes we want to use the location to render
122−-- something other than the given 'Pat'.
123−--
124−-- If given 'Pat' does not contain a location, we error out.
125−--
126−-- This should become unnecessary if
127−-- <https://gitlab.haskell.org/ghc/ghc/issues/17330> is ever fixed.
128−locatedPat ::
129− (Data (Pat pass), XXPat pass ~ Located (Pat pass)) =>
130− Pat pass ->
131− (Pat pass -> R ()) ->
132− R ()
133−locatedPat p f = case p of
134− XPat pat -> located pat f
135− _ -> error "locatedPat: Pat does not contain a location"
136−
137113-- | Set layout according to combination of given 'SrcSpan's for a given.
138114-- Use this only when you need to set layout based on e.g. combined span of
139115-- several elements when there is no corresponding 'Located' wrapper
@@ -209,10 +185,12 @@ sepSemi f xs = vlayout singleLine multiLine
209185 xs' ->
210186 if ub
211187 then do
212− txt "{ "
213− sep (txt "; ") (dontUseBraces . f) xs'
214− txt " }"
215− else sep (txt "; ") f xs'
188+ txt "{"
189+ space
190+ sep (txt ";" >> space) (dontUseBraces . f) xs'
191+ space
192+ txt "}"
193+ else sep (txt ";" >> space) f xs'
216194 multiLine =
217195 sep newline (dontUseBraces . f) xs
218196
@@ -225,6 +203,7 @@ data BracketStyle
225203 N
226204 | -- | Shifted one level
227205 S
206+ deriving (Eq, Show)
228207
229208-- | Surround given entity by backticks.
230209backticks :: R () -> R ()
@@ -301,9 +280,7 @@ brackets_ needBreaks open close style m = sitcc (vlayout singleLine multiLine)
301280 then newline >> inci m
302281 else space >> sitcc m
303282 newline
304− case style of
305− N -> txt close
306− S -> inci (txt close)
283+ inciIf (style == S) (txt close)
307284
308285----------------------------------------------------------------------------
309286-- Literals
@@ -311,3 +288,11 @@ brackets_ needBreaks open close style m = sitcc (vlayout singleLine multiLine)
311288-- | Print @,@.
312289comma :: R ()
313290comma = txt ","
291+
292+-- | Delimiting combination with 'comma'. To be used with 'sep'.
293+commaDel :: R ()
294+commaDel = comma >> breakpoint
295+
296+-- | Print @=@. Do not use @'txt' "="@.
297+equals :: R ()
298+equals = interferingTxt "="
src/Ormolu/Printer/Comments.hs+69−78
@@ -7,17 +7,17 @@ module Ormolu.Printer.Comments
77 ( spitPrecedingComments,
88 spitFollowingComments,
99 spitRemainingComments,
10+ spitCommentNow,
11+ spitCommentPending,
1012 )
1113where
1214
1315import Control.Monad
14−import Data.Coerce (coerce)
15−import Data.Data (Data)
1616import qualified Data.List.NonEmpty as NE
17+import Data.Maybe (listToMaybe)
1718import qualified Data.Text as T
1819import Ormolu.Parser.CommentStream
1920import Ormolu.Printer.Internal
20−import Ormolu.Utils (isModule)
2121import SrcLoc
2222
2323----------------------------------------------------------------------------
@@ -25,105 +25,91 @@ import SrcLoc
2525
2626-- | Output all preceding comments for an element at given location.
2727spitPrecedingComments ::
28− Data a =>
29− -- | AST element to attach comments to
30− RealLocated a ->
28+ -- | Span of the element to attach comments to
29+ RealSrcSpan ->
3130 R ()
3231spitPrecedingComments ref = do
33− r <- getLastCommentSpan
34− case r of
35− Just (Just Pipe, _) ->
36− -- We should not insert comments between pipe Haddock and the thing
37− -- it's attached to.
38− return ()
39− _ -> do
40− gotSome <- handleCommentSeries (spitPrecedingComment ref)
41− when gotSome $ do
42− -- Insert a blank line between the preceding comments and the thing
43− -- after them if there was a blank line in the input.
44− lastSpn <- fmap snd <$> getLastCommentSpan
45− when (needsNewlineBefore (getRealSrcSpan ref) lastSpn) newline
32+ gotSome <- handleCommentSeries (spitPrecedingComment ref)
33+ when gotSome $ do
34+ lastMark <- getSpanMark
35+ -- Insert a blank line between the preceding comments and the thing
36+ -- after them if there was a blank line in the input.
37+ when (needsNewlineBefore ref lastMark) newline
4638
4739-- | Output all comments following an element at given location.
4840spitFollowingComments ::
49− Data a =>
50− -- | AST element of attach comments to
51− RealLocated a ->
41+ -- | Span of the element to attach comments to
42+ RealSrcSpan ->
5243 R ()
5344spitFollowingComments ref = do
54− trimSpanStream (getRealSrcSpan ref)
45+ trimSpanStream ref
5546 void $ handleCommentSeries (spitFollowingComment ref)
5647
5748-- | Output all remaining comments in the comment stream.
5849spitRemainingComments :: R ()
59−spitRemainingComments = void $ handleCommentSeries spitRemainingComment
50+spitRemainingComments = do
51+ -- Make sure we have a blank a line between the last definition and the
52+ -- trailing comments.
53+ newline
54+ void $ handleCommentSeries spitRemainingComment
6055
6156----------------------------------------------------------------------------
6257-- Single-comment functions
6358
6459-- | Output a single preceding comment for an element at given location.
6560spitPrecedingComment ::
66− Data a =>
67− -- | AST element to attach comments to
68− RealLocated a ->
69− -- | Location of last comment in the series
70− Maybe RealSrcSpan ->
61+ -- | Span of the element to attach comments to
62+ RealSrcSpan ->
7163 -- | Are we done?
7264 R Bool
73−spitPrecedingComment (L ref a) mlastSpn = do
65+spitPrecedingComment ref = do
66+ mlastMark <- getSpanMark
7467 let p (L l _) = realSrcSpanEnd l <= realSrcSpanStart ref
7568 withPoppedComment p $ \l comment -> do
76− dirtyLine <-
77− case mlastSpn of
78− -- NOTE When the current line is dirty it means that something that
79− -- can have comments attached to it is already on the line. To avoid
80− -- problems with idempotence we cannot output the first comment
81− -- immediately because it'll be attached to the previous element (on
82− -- the same line) on the next run, so we play safe here and output
83− -- an extra 'newline' in this case.
84− Nothing -> isLineDirty -- only for very first preceding comment
85− Just _ -> return False
86− when (dirtyLine || needsNewlineBefore l mlastSpn) newline
69+ lineSpans <- thisLineSpans
70+ let thisCommentLine = srcLocLine (realSrcSpanStart l)
71+ needsNewline =
72+ case listToMaybe lineSpans of
73+ Nothing -> False
74+ Just spn -> srcLocLine (realSrcSpanEnd spn) /= thisCommentLine
75+ when (needsNewline || needsNewlineBefore l mlastMark) newline
8776 spitCommentNow l comment
88− if theSameLinePre l ref && not (isModule a)
77+ if theSameLinePre l ref
8978 then space
9079 else newline
9180
9281-- | Output a comment that follows element at given location immediately on
9382-- the same line, if there is any.
9483spitFollowingComment ::
95− Data a =>
9684 -- | AST element to attach comments to
97− RealLocated a ->
98− -- | Location of last comment in the series
99− Maybe RealSrcSpan ->
85+ RealSrcSpan ->
10086 -- | Are we done?
10187 R Bool
102−spitFollowingComment (L ref a) mlastSpn = do
88+spitFollowingComment ref = do
89+ mlastMark <- getSpanMark
10390 mnSpn <- nextEltSpan
10491 -- Get first enclosing span that is not equal to reference span, i.e. it's
10592 -- truly something enclosing the AST element.
10693 meSpn <- getEnclosingSpan (/= ref)
107− withPoppedComment (commentFollowsElt ref mnSpn meSpn mlastSpn) $ \l comment ->
108− if theSameLinePost l ref && not (isModule a)
94+ withPoppedComment (commentFollowsElt ref mnSpn meSpn mlastMark) $ \l comment ->
95+ if theSameLinePost l ref
10996 then
11097 if isMultilineComment comment
11198 then space >> spitCommentNow l comment
11299 else spitCommentPending OnTheSameLine l comment
113100 else do
114− when (needsNewlineBefore l mlastSpn) $
101+ when (needsNewlineBefore l mlastMark) $
115102 registerPendingCommentLine OnNextLine ""
116103 spitCommentPending OnNextLine l comment
117104
118105-- | Output a single remaining comment from the comment stream.
119106spitRemainingComment ::
120− -- | Location of last comment in the series
121− Maybe RealSrcSpan ->
122107 -- | Are we done?
123108 R Bool
124−spitRemainingComment mlastSpn =
109+spitRemainingComment = do
110+ mlastMark <- getSpanMark
125111 withPoppedComment (const True) $ \l comment -> do
126− when (needsNewlineBefore l mlastSpn) newline
112+ when (needsNewlineBefore l mlastMark) newline
127113 spitCommentNow l comment
128114 newline
129115
@@ -134,13 +120,13 @@ spitRemainingComment mlastSpn =
134120handleCommentSeries ::
135121 -- | Given location of previous comment, output the next comment
136122 -- returning 'True' if we're done
137− (Maybe RealSrcSpan -> R Bool) ->
123+ R Bool ->
138124 -- | Whether we printed any comments
139125 R Bool
140126handleCommentSeries f = go False
141127 where
142128 go gotSome = do
143− done <- getLastCommentSpan >>= f . fmap snd
129+ done <- f
144130 if done
145131 then return gotSome
146132 else go True
@@ -166,13 +152,14 @@ needsNewlineBefore ::
166152 -- | Current comment span
167153 RealSrcSpan ->
168154 -- | Last printed comment span
169− Maybe RealSrcSpan ->
155+ Maybe SpanMark ->
170156 Bool
171−needsNewlineBefore l mlastSpn =
172− case mlastSpn of
157+needsNewlineBefore _ (Just (HaddockSpan _ _)) = True
158+needsNewlineBefore l mlastMark =
159+ case spanMarkSpan <$> mlastMark of
173160 Nothing -> False
174− Just lastSpn ->
175− srcSpanStartLine l > srcSpanEndLine lastSpn + 1
161+ Just lastMark ->
162+ srcSpanStartLine l > srcSpanEndLine lastMark + 1
176163
177164-- | Is the preceding comment and AST element are on the same line?
178165theSameLinePre ::
@@ -203,11 +190,11 @@ commentFollowsElt ::
203190 -- | Location of enclosing AST element
204191 Maybe RealSrcSpan ->
205192 -- | Location of last comment in the series
206− Maybe RealSrcSpan ->
193+ Maybe SpanMark ->
207194 -- | Comment to test
208195 RealLocated Comment ->
209196 Bool
210−commentFollowsElt ref mnSpn meSpn mlastSpn (L l comment) =
197+commentFollowsElt ref mnSpn meSpn mlastMark (L l comment) =
211198 -- A comment follows a AST element if all 4 conditions are satisfied:
212199 goesAfter
213200 && logicallyFollows
@@ -220,9 +207,8 @@ commentFollowsElt ref mnSpn meSpn mlastSpn (L l comment) =
220207 -- 2) The comment logically belongs to the element, four cases:
221208 logicallyFollows =
222209 theSameLinePost l ref -- a) it's on the same line
223− || isPrevHaddock comment -- b) it's a Haddock string starting with -- ^
224− || continuation -- c) it's a continuation of a comment block
225− || lastInEnclosing -- d) it's the last element in the enclosing construct
210+ || continuation -- b) it's a continuation of a comment block
211+ || lastInEnclosing -- c) it's the last element in the enclosing construct
226212
227213 -- 3) There is no other AST element between this element and the comment:
228214 noEltBetween =
@@ -244,15 +230,20 @@ commentFollowsElt ref mnSpn meSpn mlastSpn (L l comment) =
244230 Nothing -> True
245231 Just espn ->
246232 let startColumn = srcLocCol . realSrcSpanStart
247− in if startColumn espn > startColumn ref
248− then True
249− else
250− abs (startColumn espn - startColumn l)
251− >= abs (startColumn ref - startColumn l)
233+ in startColumn espn > startColumn ref
234+ || ( abs (startColumn espn - startColumn l)
235+ >= abs (startColumn ref - startColumn l)
236+ )
252237 continuation =
253− case mlastSpn of
254− Nothing -> False
255− Just spn -> srcSpanEndLine spn + 1 == srcSpanStartLine l
238+ -- A comment is a continuation when it doesn't have non-whitespace
239+ -- lexemes in front of it and goes right after the previous comment.
240+ not (hasAtomsBefore comment)
241+ && ( case mlastMark of
242+ Just (HaddockSpan _ _) -> False
243+ Just (CommentSpan spn) ->
244+ srcSpanEndLine spn + 1 == srcSpanStartLine l
245+ _ -> False
246+ )
256247 lastInEnclosing =
257248 case meSpn of
258249 -- When there is no enclosing element, return false
@@ -274,9 +265,9 @@ spitCommentNow spn comment = do
274265 . sequence_
275266 . NE.intersperse newline
276267 . fmap (txt . T.pack)
277− . coerce
268+ . unComment
278269 $ comment
279− setLastCommentSpan Nothing spn
270+ setSpanMark (CommentSpan spn)
280271
281272-- | Output a 'Comment' at the end of correct line or after it depending on
282273-- 'CommentPosition'. Used for comments that may potentially follow on the
@@ -290,6 +281,6 @@ spitCommentPending position spn comment = do
290281 . sequence_
291282 . NE.toList
292283 . fmap (registerPendingCommentLine position . T.pack)
293− . coerce
284+ . unComment
294285 $ comment
295− setLastCommentSpan Nothing spn
286+ setSpanMark (CommentSpan spn)
src/Ormolu/Printer/Internal.hs+173−109
@@ -14,10 +14,11 @@ module Ormolu.Printer.Internal
1414
1515 -- * Internal functions
1616 txt,
17+ interferingTxt,
1718 atom,
1819 space,
1920 newline,
20− isLineDirty,
21+ useRecordDot,
2122 inci,
2223 sitcc,
2324 Layout (..),
@@ -38,9 +39,14 @@ module Ormolu.Printer.Internal
3839 popComment,
3940 getEnclosingSpan,
4041 withEnclosingSpan,
42+ thisLineSpans,
43+
44+ -- * Stateful markers
45+ SpanMark (..),
46+ spanMarkSpan,
4147 HaddockStyle (..),
42− setLastCommentSpan,
43− getLastCommentSpan,
48+ setSpanMark,
49+ getSpanMark,
4450
4551 -- * Annotations
4652 getAnns,
@@ -73,43 +79,44 @@ newtype R a = R (ReaderT RC (State SC) a)
7379
7480-- | Reader context of 'R'. This should be used when we control rendering by
7581-- enclosing certain expressions with wrappers.
76−data RC
77− = RC
78− { -- | Indentation level, as the column index we need to start from after
79− -- a newline if we break lines
80− rcIndent :: !Int,
81− -- | Current layout
82− rcLayout :: Layout,
83− -- | Spans of enclosing elements of AST
84− rcEnclosingSpans :: [RealSrcSpan],
85− -- | Collection of annotations
86− rcAnns :: Anns,
87− -- | If the last expression in the layout can use braces
88− rcCanUseBraces :: Bool
89− }
82+data RC = RC
83+ { -- | Indentation level, as the column index we need to start from after
84+ -- a newline if we break lines
85+ rcIndent :: !Int,
86+ -- | Current layout
87+ rcLayout :: Layout,
88+ -- | Spans of enclosing elements of AST
89+ rcEnclosingSpans :: [RealSrcSpan],
90+ -- | Collection of annotations
91+ rcAnns :: Anns,
92+ -- | Whether the last expression in the layout can use braces
93+ rcCanUseBraces :: Bool,
94+ -- | Whether the source could have used the record dot preprocessor
95+ rcUseRecDot :: Bool
96+ }
9097
9198-- | State context of 'R'.
92−data SC
93− = SC
94− { -- | Index of the next column to render
95− scColumn :: !Int,
96− -- | Rendered source code so far
97− scBuilder :: Builder,
98− -- | Span stream
99− scSpanStream :: SpanStream,
100− -- | Comment stream
101− scCommentStream :: CommentStream,
102− -- | Pending comment lines (in reverse order) to be inserted before next
103− -- newline, 'Int' is the indentation level
104− scPendingComments :: ![(CommentPosition, Int, Text)],
105− -- | Whether the current line is “dirty”, that is, already contains
106− -- atoms that can have comments attached to them
107− scDirtyLine :: !Bool,
108− -- | Whether to output a space before the next output
109− scRequestedDelimiter :: !RequestedDelimiter,
110− -- | Span of last output comment
111− scLastCommentSpan :: !(Maybe (Maybe HaddockStyle, RealSrcSpan))
112− }
99+data SC = SC
100+ { -- | Index of the next column to render
101+ scColumn :: !Int,
102+ -- | Indentation level that was used for the current line
103+ scIndent :: !Int,
104+ -- | Rendered source code so far
105+ scBuilder :: Builder,
106+ -- | Span stream
107+ scSpanStream :: SpanStream,
108+ -- | Spans of atoms that have been printed on the current line so far
109+ scThisLineSpans :: [RealSrcSpan],
110+ -- | Comment stream
111+ scCommentStream :: CommentStream,
112+ -- | Pending comment lines (in reverse order) to be inserted before next
113+ -- newline, 'Int' is the indentation level
114+ scPendingComments :: ![(CommentPosition, Text)],
115+ -- | Whether to output a space before the next output
116+ scRequestedDelimiter :: !RequestedDelimiter,
117+ -- | An auxiliary marker for keeping track of last output element
118+ scSpanMark :: !(Maybe SpanMark)
119+ }
113120
114121-- | Make sure next output is delimited by one of the following.
115122data RequestedDelimiter
@@ -151,32 +158,55 @@ runR ::
151158 CommentStream ->
152159 -- | Annotations
153160 Anns ->
161+ -- | Use Record Dot Syntax
162+ Bool ->
154163 -- | Resulting rendition
155164 Text
156−runR (R m) sstream cstream anns =
165+runR (R m) sstream cstream anns recDot =
157166 TL.toStrict . toLazyText . scBuilder $ execState (runReaderT m rc) sc
158167 where
159− rc = RC
160− { rcIndent = 0,
161− rcLayout = MultiLine,
162− rcEnclosingSpans = [],
163− rcAnns = anns,
164− rcCanUseBraces = False
165− }
166− sc = SC
167− { scColumn = 0,
168− scBuilder = mempty,
169− scSpanStream = sstream,
170− scCommentStream = cstream,
171− scPendingComments = [],
172− scDirtyLine = False,
173− scRequestedDelimiter = VeryBeginning,
174− scLastCommentSpan = Nothing
175− }
168+ rc =
169+ RC
170+ { rcIndent = 0,
171+ rcLayout = MultiLine,
172+ rcEnclosingSpans = [],
173+ rcAnns = anns,
174+ rcCanUseBraces = False,
175+ rcUseRecDot = recDot
176+ }
177+ sc =
178+ SC
179+ { scColumn = 0,
180+ scIndent = 0,
181+ scBuilder = mempty,
182+ scSpanStream = sstream,
183+ scThisLineSpans = [],
184+ scCommentStream = cstream,
185+ scPendingComments = [],
186+ scRequestedDelimiter = VeryBeginning,
187+ scSpanMark = Nothing
188+ }
176189
177190----------------------------------------------------------------------------
178191-- Internal functions
179192
193+-- | Type of the thing to output. Influences the primary low-level rendering
194+-- function 'spit'.
195+data SpitType
196+ = -- | Simple opaque text that breaks comment series.
197+ SimpleText
198+ | -- | Like 'SimpleText', but assume that when this text is inserted it
199+ -- will separate an 'Atom' and its pending comments, so insert an extra
200+ -- 'newline' in that case to force the pending comments and continue on
201+ -- a fresh line.
202+ InterferingText
203+ | -- | An atom that typically have span information in the AST and can
204+ -- have comments attached to it.
205+ Atom
206+ | -- | Used for rendering comment lines.
207+ CommentPart
208+ deriving (Show, Eq)
209+
180210-- | Output a fixed 'Text' fragment. The argument may not contain any line
181211-- breaks. 'txt' is used to output all sorts of “fixed” bits of syntax like
182212-- keywords and pipes @|@ in functional dependencies.
@@ -188,7 +218,15 @@ txt ::
188218 -- | 'Text' to output
189219 Text ->
190220 R ()
191−txt = spit False False
221+txt = spit SimpleText
222+
223+-- | Similar to 'txt' but the text inserted this way is assumed to break the
224+-- “link” between the preceding atom and its pending comments.
225+interferingTxt ::
226+ -- | 'Text' to output
227+ Text ->
228+ R ()
229+interferingTxt = spit InterferingText
192230
193231-- | Output 'Outputable' fragment of AST. This can be used to output numeric
194232-- literals and similar. Everything that doesn't have inner structure but
@@ -197,48 +235,61 @@ atom ::
197235 Outputable a =>
198236 a ->
199237 R ()
200−atom = spit True False . T.pack . showOutputable
238+atom = spit Atom . T.pack . showOutputable
201239
202240-- | Low-level non-public helper to define 'txt' and 'atom'.
203241spit ::
204− -- | Should we mark the line as dirty?
205− Bool ->
206− -- | Used during outputting of pending comments?
207− Bool ->
242+ -- | Type of the thing to spit
243+ SpitType ->
208244 -- | 'Text' to output
209245 Text ->
210246 R ()
211−spit dirty printingComments txt' = do
247+spit _ "" = return ()
248+spit stype text = do
212249 requestedDel <- R (gets scRequestedDelimiter)
250+ pendingComments <- R (gets scPendingComments)
251+ when (stype == InterferingText && not (null pendingComments)) newline
213252 case requestedDel of
214253 RequestedNewline -> do
215254 R . modify $ \sc ->
216255 sc
217256 { scRequestedDelimiter = RequestedNothing
218257 }
219− if printingComments
220− then newlineRaw
221− else newline
258+ case stype of
259+ CommentPart -> newlineRaw
260+ _ -> newline
222261 _ -> return ()
223262 R $ do
224263 i <- asks rcIndent
225264 c <- gets scColumn
226− let spaces =
227− if c < i
228− then T.replicate (i - c) " "
229− else bool mempty " " (requestedDel == RequestedSpace)
230− indentedTxt = spaces <> txt'
265+ closestEnclosing <- listToMaybe <$> asks rcEnclosingSpans
266+ let indentedTxt = spaces <> text
267+ spaces = T.replicate spacesN " "
268+ spacesN =
269+ if c == 0
270+ then i
271+ else bool 0 1 (requestedDel == RequestedSpace)
231272 modify $ \sc ->
232273 sc
233274 { scBuilder = scBuilder sc <> fromText indentedTxt,
234275 scColumn = scColumn sc + T.length indentedTxt,
235− scDirtyLine = scDirtyLine sc || dirty,
276+ scIndent =
277+ if c == 0
278+ then i
279+ else scIndent sc,
280+ scThisLineSpans =
281+ let xs = scThisLineSpans sc
282+ in case stype of
283+ Atom -> case closestEnclosing of
284+ Nothing -> xs
285+ Just x -> x : xs
286+ _ -> xs,
236287 scRequestedDelimiter = RequestedNothing,
237− scLastCommentSpan =
238− -- NOTE If there are pending comments, do not reset last comment
288+ scSpanMark =
289+ -- If there are pending comments, do not reset last comment
239290 -- location.
240− if printingComments || (not . null . scPendingComments) sc
241− then scLastCommentSpan sc
291+ if (stype == CommentPart) || (not . null . scPendingComments) sc
292+ then scSpanMark sc
242293 else Nothing
243294 }
244295
@@ -269,21 +320,22 @@ space = R . modify $ \sc ->
269320-- hard to output more than one blank newline in a row.
270321newline :: R ()
271322newline = do
323+ indent <- R (gets scIndent)
272324 cs <- reverse <$> R (gets scPendingComments)
273325 case cs of
274326 [] -> newlineRaw
275− ((position, _, _) : _) -> do
327+ ((position, _) : _) -> do
276328 case position of
277329 OnTheSameLine -> space
278330 OnNextLine -> newlineRaw
279− R . forM_ cs $ \(_, indent, txt') ->
331+ R . forM_ cs $ \(_, text) ->
280332 let modRC rc =
281333 rc
282334 { rcIndent = indent
283335 }
284336 R m = do
285− unless (T.null txt') $
286− spit False True txt'
337+ unless (T.null text) $
338+ spit CommentPart text
287339 newlineRaw
288340 in local modRC m
289341 R . modify $ \sc ->
@@ -304,7 +356,8 @@ newlineRaw = R . modify $ \sc ->
304356 VeryBeginning -> builderSoFar
305357 _ -> builderSoFar <> "\n",
306358 scColumn = 0,
307− scDirtyLine = False,
359+ scIndent = 0,
360+ scThisLineSpans = [],
308361 scRequestedDelimiter = case scRequestedDelimiter sc of
309362 AfterNewline -> RequestedNewline
310363 RequestedNewline -> RequestedNewline
@@ -312,10 +365,9 @@ newlineRaw = R . modify $ \sc ->
312365 _ -> AfterNewline
313366 }
314367
315−-- | Check if the current line is “dirty”, that is, there is something on it
316−-- that can have comments attached to it.
317−isLineDirty :: R Bool
318−isLineDirty = R (gets scDirtyLine)
368+-- | Return 'True' if we should print record dot syntax.
369+useRecordDot :: R Bool
370+useRecordDot = R (asks rcUseRecDot)
319371
320372-- | Increase indentation level by one indentation step for the inner
321373-- computation. 'inci' should be used when a part of code must be more
@@ -332,8 +384,7 @@ inci (R m) = R (local modRC m)
332384
333385-- | Set indentation level for the inner computation equal to current
334386-- column. This makes sure that the entire inner block is uniformly
335−-- \"shifted\" to the right. Only works (and makes sense) when enclosing
336−-- layout is multi-line.
387+-- \"shifted\" to the right.
337388sitcc :: R () -> R ()
338389sitcc (R m) = do
339390 requestedDel <- R (gets scRequestedDelimiter)
@@ -341,16 +392,9 @@ sitcc (R m) = do
341392 c <- R (gets scColumn)
342393 let modRC rc =
343394 rc
344− { rcIndent = max i c + bool 0 1 (requestedDel == RequestedSpace)
395+ { rcIndent = max i (c + bool 0 1 (requestedDel == RequestedSpace))
345396 }
346− vlayout (R m) . R $ do
347− modify $ \sc ->
348− sc
349− { scRequestedDelimiter = case requestedDel of
350− RequestedSpace -> RequestedNothing
351− other -> other
352− }
353− local modRC m
397+ R (local modRC m)
354398
355399-- | Set 'Layout' for internal computation.
356400enterLayout :: Layout -> R () -> R ()
@@ -391,11 +435,10 @@ registerPendingCommentLine ::
391435 -- | 'Text' to output
392436 Text ->
393437 R ()
394−registerPendingCommentLine position txt' = R $ do
395− i <- asks rcIndent
438+registerPendingCommentLine position text = R $ do
396439 modify $ \sc ->
397440 sc
398− { scPendingComments = (position, i, txt') : scPendingComments sc
441+ { scPendingComments = (position, text) : scPendingComments sc
399442 }
400443
401444-- | Drop elements that begin before or at the same place as given
@@ -454,6 +497,29 @@ withEnclosingSpan spn (R m) = R (local modRC m)
454497 { rcEnclosingSpans = spn : rcEnclosingSpans rc
455498 }
456499
500+-- | Get spans on this line so far.
501+thisLineSpans :: R [RealSrcSpan]
502+thisLineSpans = R (gets scThisLineSpans)
503+
504+----------------------------------------------------------------------------
505+-- Stateful markers
506+
507+-- | An auxiliary marker for keeping track of last output element.
508+data SpanMark
509+ = -- | Haddock comment
510+ HaddockSpan HaddockStyle RealSrcSpan
511+ | -- | Non-haddock comment
512+ CommentSpan RealSrcSpan
513+ | -- | A statement in a do-block and such span
514+ StatementSpan RealSrcSpan
515+
516+-- | Project 'RealSrcSpan' from 'SpanMark'.
517+spanMarkSpan :: SpanMark -> RealSrcSpan
518+spanMarkSpan = \case
519+ HaddockSpan _ s -> s
520+ CommentSpan s -> s
521+ StatementSpan s -> s
522+
457523-- | Haddock string style.
458524data HaddockStyle
459525 = -- | @-- |@
@@ -466,20 +532,18 @@ data HaddockStyle
466532 Named String
467533
468534-- | Set span of last output comment.
469−setLastCommentSpan ::
470− -- | 'HaddockStyle' or 'Nothing' if it's a non-Haddock comment
471− Maybe HaddockStyle ->
472− -- | Location of last printed comment
473− RealSrcSpan ->
535+setSpanMark ::
536+ -- | Span mark to set
537+ SpanMark ->
474538 R ()
475−setLastCommentSpan mhStyle spn = R . modify $ \sc ->
539+setSpanMark spnMark = R . modify $ \sc ->
476540 sc
477− { scLastCommentSpan = Just (mhStyle, spn)
541+ { scSpanMark = Just spnMark
478542 }
479543
480544-- | Get span of last output comment.
481−getLastCommentSpan :: R (Maybe (Maybe HaddockStyle, RealSrcSpan))
482−getLastCommentSpan = R (gets scLastCommentSpan)
545+getSpanMark :: R (Maybe SpanMark)
546+getSpanMark = R (gets scSpanMark)
483547
484548----------------------------------------------------------------------------
485549-- Annotations
@@ -503,7 +567,7 @@ dontUseBraces (R r) = R (local (\i -> i {rcCanUseBraces = False}) r)
503567
504568-- | Return 'True' if we can use braces in this context.
505569canUseBraces :: R Bool
506−canUseBraces = R $ asks rcCanUseBraces
570+canUseBraces = R (asks rcCanUseBraces)
507571
508572----------------------------------------------------------------------------
509573-- Constants
src/Ormolu/Printer/Meat/Common.hs+20−18
@@ -17,14 +17,12 @@ where
1717
1818import Control.Monad
1919import Data.List (isPrefixOf)
20−import Data.Maybe (isJust)
2120import qualified Data.Text as T
2221import GHC hiding (GhcPs, IE)
2322import Name (nameStableString)
2423import OccName (OccName (..))
2524import Ormolu.Printer.Combinators
2625import Ormolu.Utils
27−import RdrName (RdrName (..))
2826
2927-- | Data and type family style.
3028data FamilyStyle
@@ -76,10 +74,10 @@ p_rdrName l@(L spn _) = located l $ \x -> do
7674 Qual mname occName ->
7775 p_qualName mname occName
7876 Orig _ occName ->
79− -- NOTE This is used when GHC generates code that will be fed
80− -- into the renamer (e.g. from deriving clauses), but where we
81− -- want to say that something comes from given module which is
82− -- not specified in the source code, e.g. @Prelude.map@.
77+ -- This is used when GHC generates code that will be fed into
78+ -- the renamer (e.g. from deriving clauses), but where we want
79+ -- to say that something comes from given module which is not
80+ -- specified in the source code, e.g. @Prelude.map@.
8381 --
8482 -- My current understanding is that the provided module name
8583 -- serves no purpose for us and can be safely ignored.
@@ -99,9 +97,9 @@ doesNotNeedExtraParens :: RdrName -> Bool
9997doesNotNeedExtraParens = \case
10098 Exact name ->
10199 let s = nameStableString name
102− in -- NOTE I'm not sure this "stable string" is stable enough, but it looks
103− -- like this is the most robust way to tell if we're looking at exactly
104− -- this piece of built-in syntax.
100+ in -- I'm not sure this "stable string" is stable enough, but it looks
101+ -- like this is the most robust way to tell if we're looking at
102+ -- exactly this piece of built-in syntax.
105103 ("$ghc-prim$GHC.Tuple$" `isPrefixOf` s)
106104 || ("$ghc-prim$GHC.Types$[]" `isPrefixOf` s)
107105 _ -> False
@@ -116,14 +114,14 @@ p_qualName mname occName = do
116114p_infixDefHelper ::
117115 -- | Whether to format in infix style
118116 Bool ->
119− -- | Indentation-bumping wrapper
120− (R () -> R ()) ->
117+ -- | Whether to bump indentation for arguments
118+ Bool ->
121119 -- | How to print the operator\/name
122120 R () ->
123121 -- | How to print the arguments
124122 [R ()] ->
125123 R ()
126−p_infixDefHelper isInfix inci' name args =
124+p_infixDefHelper isInfix indentArgs name args =
127125 case (isInfix, args) of
128126 (True, p0 : p1 : ps) -> do
129127 let parens' =
@@ -133,18 +131,18 @@ p_infixDefHelper isInfix inci' name args =
133131 parens' $ do
134132 p0
135133 breakpoint
136− inci $ sitcc $ do
134+ inci . sitcc $ do
137135 name
138136 space
139137 p1
140− unless (null ps) . inci' $ do
138+ unless (null ps) . inciIf indentArgs $ do
141139 breakpoint
142140 sitcc (sep breakpoint sitcc ps)
143141 (_, ps) -> do
144142 name
145143 unless (null ps) $ do
146144 breakpoint
147− inci' $ sitcc (sep breakpoint sitcc args)
145+ inciIf indentArgs $ sitcc (sep breakpoint sitcc args)
148146
149147-- | Print a Haddock.
150148p_hsDocString ::
@@ -156,7 +154,11 @@ p_hsDocString ::
156154 LHsDocString ->
157155 R ()
158156p_hsDocString hstyle needsNewline (L l str) = do
159− goesAfterComment <- isJust <$> getLastCommentSpan
157+ let isCommentSpan = \case
158+ HaddockSpan _ _ -> True
159+ CommentSpan _ -> True
160+ _ -> False
161+ goesAfterComment <- maybe False isCommentSpan <$> getSpanMark
160162 -- Make sure the Haddock is separated by a newline from other comments.
161163 when goesAfterComment newline
162164 forM_ (zip (splitDocString str) (True : repeat False)) $ \(x, isFirst) -> do
@@ -175,8 +177,8 @@ p_hsDocString hstyle needsNewline (L l str) = do
175177 -- It's often the case that the comment itself doesn't have a span
176178 -- attached to it and instead its location can be obtained from
177179 -- nearest enclosing span.
178− getEnclosingSpan (const True) >>= mapM_ (setLastCommentSpan (Just hstyle))
179− RealSrcSpan spn -> setLastCommentSpan (Just hstyle) spn
180+ getEnclosingSpan (const True) >>= mapM_ (setSpanMark . HaddockSpan hstyle)
181+ RealSrcSpan spn -> setSpanMark (HaddockSpan hstyle spn)
180182
181183-- | Print anchor of named doc section.
182184p_hsDocName :: String -> R ()
src/Ormolu/Printer/Meat/Declaration.hs+175−110
@@ -7,12 +7,12 @@
77-- | Rendering of declarations.
88module Ormolu.Printer.Meat.Declaration
99 ( p_hsDecls,
10− hasSeparatedDecls,
10+ p_hsDeclsRespectGrouping,
1111 )
1212where
1313
1414import Data.List (sort)
15−import Data.List.NonEmpty ((<|), NonEmpty (..))
15+import Data.List.NonEmpty (NonEmpty (..), (<|))
1616import qualified Data.List.NonEmpty as NE
1717import GHC hiding (InlinePragma)
1818import OccName (occNameFS)
@@ -34,25 +34,57 @@ import Ormolu.Printer.Meat.Declaration.Value
3434import Ormolu.Printer.Meat.Declaration.Warning
3535import Ormolu.Printer.Meat.Type
3636import Ormolu.Utils
37−import RdrName (rdrNameOcc)
37+
38+data UserGrouping
39+ = -- | Always put newlines where we think they should be
40+ Disregard
41+ | -- | Respect user preferences regarding grouping
42+ Respect
43+ deriving (Eq, Show)
3844
3945p_hsDecls :: FamilyStyle -> [LHsDecl GhcPs] -> R ()
40−p_hsDecls style decls = sepSemi id $
41− -- Return a list of rendered declarations, adding a newline to separate
42− -- groups.
43− case groupDecls decls of
44− [] -> []
45− (x : xs) ->
46− NE.toList (renderGroup x)
47− ++ concatMap (NE.toList . separateGroup . renderGroup) xs
46+p_hsDecls = p_hsDecls' Disregard
47+
48+-- | Like 'p_hsDecls' but respects user choices regarding grouping. If the
49+-- user omits newlines between declarations, we also omit them in most
50+-- cases, except when said declarations have associated Haddocks.
51+--
52+-- Does some normalization (compress subsequent newlines into a single one)
53+p_hsDeclsRespectGrouping :: FamilyStyle -> [LHsDecl GhcPs] -> R ()
54+p_hsDeclsRespectGrouping = p_hsDecls' Respect
55+
56+p_hsDecls' :: UserGrouping -> FamilyStyle -> [LHsDecl GhcPs] -> R ()
57+p_hsDecls' grouping style decls =
58+ sepSemi id $
59+ -- Return a list of rendered declarations, adding a newline to separate
60+ -- groups.
61+ case groupDecls decls of
62+ [] -> []
63+ (x : xs) -> renderGroup x ++ concat (zipWith renderGroupWithPrev (x : xs) xs)
4864 where
49− renderGroup = fmap (located' $ dontUseBraces . p_hsDecl style)
50− separateGroup (x :| xs) = (breakpoint' >> x) :| xs
65+ renderGroup = NE.toList . fmap (located' $ dontUseBraces . p_hsDecl style)
66+ renderGroupWithPrev prev curr =
67+ -- We can omit a blank line when the user didn't add one, but we must
68+ -- ensure we always add blank lines around documented declarations
69+ case grouping of
70+ Disregard ->
71+ breakpoint : renderGroup curr
72+ Respect ->
73+ if separatedByBlankNE getLoc prev curr
74+ || isDocumented prev
75+ || isDocumented curr
76+ then breakpoint : renderGroup curr
77+ else renderGroup curr
78+
79+-- | Is a declaration group documented?
80+isDocumented :: NonEmpty (LHsDecl GhcPs) -> Bool
81+isDocumented = any (isHaddock . unLoc)
82+ where
83+ isHaddock DocNext = True
84+ isHaddock DocPrev = True
85+ isHaddock _ = False
5186
5287-- | Group relevant declarations together.
53−--
54−-- Add a declaration to a group iff it is relevant to either the first or
55−-- the last declaration of the group.
5688groupDecls :: [LHsDecl GhcPs] -> [NonEmpty (LHsDecl GhcPs)]
5789groupDecls [] = []
5890groupDecls (l@(L _ DocNext) : xs) =
@@ -61,44 +93,40 @@ groupDecls (l@(L _ DocNext) : xs) =
6193 case groupDecls xs of
6294 [] -> [l :| []]
6395 (x : xs') -> (l <| x) : xs'
64−groupDecls (lhdr : xs) =
65− let -- Pick the first decl as the group header
66− hdr = unLoc lhdr
67− -- Zip rest of the decls with their previous decl
68− zipped = zip (lhdr : xs) xs
69− -- Pick decls from the tail if they are relevant to the group header
70− -- or the previous decl.
71− (grp, rest) = flip span zipped $ \(L _ prev, L _ cur) ->
72− let relevantToHdr = groupedDecls hdr cur
73− relevantToPrev = groupedDecls prev cur
74− in relevantToHdr || relevantToPrev
75− in (lhdr :| map snd grp) : groupDecls (map snd rest)
96+groupDecls (header : xs) =
97+ let (grp, rest) = flip span (zip (header : xs) xs) $ \(previous, current) ->
98+ let relevantToHdr = groupedDecls header current
99+ relevantToPrev = groupedDecls previous current
100+ isDeclSeries = declSeries previous current
101+ in isDeclSeries || relevantToHdr || relevantToPrev
102+ in (header :| map snd grp) : groupDecls (map snd rest)
76103
77104p_hsDecl :: FamilyStyle -> HsDecl GhcPs -> R ()
78105p_hsDecl style = \case
79− TyClD NoExt x -> p_tyClDecl style x
80− ValD NoExt x -> p_valDecl x
81− SigD NoExt x -> p_sigDecl x
82− InstD NoExt x -> p_instDecl style x
83− DerivD NoExt x -> p_derivDecl x
84− DefD NoExt x -> p_defaultDecl x
85− ForD NoExt x -> p_foreignDecl x
86− WarningD NoExt x -> p_warnDecls x
87− AnnD NoExt x -> p_annDecl x
88− RuleD NoExt x -> p_ruleDecls x
89− SpliceD NoExt x -> p_spliceDecl x
90− DocD NoExt docDecl ->
106+ TyClD NoExtField x -> p_tyClDecl style x
107+ ValD NoExtField x -> p_valDecl x
108+ SigD NoExtField x -> p_sigDecl x
109+ InstD NoExtField x -> p_instDecl style x
110+ DerivD NoExtField x -> p_derivDecl x
111+ DefD NoExtField x -> p_defaultDecl x
112+ ForD NoExtField x -> p_foreignDecl x
113+ WarningD NoExtField x -> p_warnDecls x
114+ AnnD NoExtField x -> p_annDecl x
115+ RuleD NoExtField x -> p_ruleDecls x
116+ SpliceD NoExtField x -> p_spliceDecl x
117+ DocD NoExtField docDecl ->
91118 case docDecl of
92119 DocCommentNext str -> p_hsDocString Pipe False (noLoc str)
93120 DocCommentPrev str -> p_hsDocString Caret False (noLoc str)
94121 DocCommentNamed name str -> p_hsDocString (Named name) False (noLoc str)
95122 DocGroup n str -> p_hsDocString (Asterisk n) False (noLoc str)
96− RoleAnnotD NoExt x -> p_roleAnnot x
97− XHsDecl _ -> notImplemented "XHsDecl"
123+ RoleAnnotD NoExtField x -> p_roleAnnot x
124+ KindSigD NoExtField s -> p_standaloneKindSig s
125+ XHsDecl x -> noExtCon x
98126
99127p_tyClDecl :: FamilyStyle -> TyClDecl GhcPs -> R ()
100128p_tyClDecl style = \case
101− FamDecl NoExt x -> p_famDecl style x
129+ FamDecl NoExtField x -> p_famDecl style x
102130 SynDecl {..} -> p_synDecl tcdLName tcdFixity tcdTyVars tcdRhs
103131 DataDecl {..} ->
104132 p_dataDecl
@@ -119,39 +147,65 @@ p_tyClDecl style = \case
119147 tcdATs
120148 tcdATDefs
121149 tcdDocs
122− XTyClDecl {} -> notImplemented "XTyClDecl"
150+ XTyClDecl x -> noExtCon x
123151
124152p_instDecl :: FamilyStyle -> InstDecl GhcPs -> R ()
125153p_instDecl style = \case
126− ClsInstD NoExt x -> p_clsInstDecl x
127− TyFamInstD NoExt x -> p_tyFamInstDecl style x
128− DataFamInstD NoExt x -> p_dataFamInstDecl style x
129− XInstDecl _ -> notImplemented "XInstDecl"
154+ ClsInstD NoExtField x -> p_clsInstDecl x
155+ TyFamInstD NoExtField x -> p_tyFamInstDecl style x
156+ DataFamInstD NoExtField x -> p_dataFamInstDecl style x
157+ XInstDecl x -> noExtCon x
130158
131159p_derivDecl :: DerivDecl GhcPs -> R ()
132160p_derivDecl = \case
133− d@DerivDecl {..} -> p_standaloneDerivDecl d
134− XDerivDecl _ -> notImplemented "XDerivDecl standalone deriving"
161+ d@DerivDecl {} -> p_standaloneDerivDecl d
162+ XDerivDecl x -> noExtCon x
135163
136164-- | Determine if these declarations should be grouped together.
137165groupedDecls ::
138− HsDecl GhcPs ->
139− HsDecl GhcPs ->
166+ LHsDecl GhcPs ->
167+ LHsDecl GhcPs ->
168+ Bool
169+groupedDecls (L l_x x') (L l_y y') =
170+ case (x', y') of
171+ (TypeSignature ns, FunctionBody ns') -> ns `intersects` ns'
172+ (TypeSignature ns, DefaultSignature ns') -> ns `intersects` ns'
173+ (DefaultSignature ns, TypeSignature ns') -> ns `intersects` ns'
174+ (DefaultSignature ns, FunctionBody ns') -> ns `intersects` ns'
175+ (x, FunctionBody ns) | Just ns' <- isPragma x -> ns `intersects` ns'
176+ (FunctionBody ns, x) | Just ns' <- isPragma x -> ns `intersects` ns'
177+ (x, DataDeclaration n) | Just ns <- isPragma x -> n `elem` ns
178+ (DataDeclaration n, x)
179+ | Just ns <- isPragma x ->
180+ let f = occNameFS . rdrNameOcc in f n `elem` map f ns
181+ (x, y)
182+ | Just ns <- isPragma x,
183+ Just ns' <- isPragma y ->
184+ ns `intersects` ns'
185+ (x, TypeSignature ns) | Just ns' <- isPragma x -> ns `intersects` ns'
186+ (TypeSignature ns, x) | Just ns' <- isPragma x -> ns `intersects` ns'
187+ (PatternSignature ns, Pattern n) -> n `elem` ns
188+ (KindSignature n, DataDeclaration n') -> n == n'
189+ (KindSignature n, ClassDeclaration n') -> n == n'
190+ (KindSignature n, FamilyDeclaration n') -> n == n'
191+ -- Special case for TH splices, we look at locations
192+ (Splice, Splice) -> not (separatedByBlank id l_x l_y)
193+ -- This looks only at Haddocks, normal comments are handled elsewhere
194+ (DocNext, _) -> True
195+ (_, DocPrev) -> True
196+ _ -> False
197+
198+-- | Detect declaration series that should not have blanks between them.
199+declSeries ::
200+ LHsDecl GhcPs ->
201+ LHsDecl GhcPs ->
140202 Bool
141−groupedDecls (TypeSignature ns) (FunctionBody ns') = ns `intersects` ns'
142−groupedDecls x (FunctionBody ns) | Just ns' <- isPragma x = ns `intersects` ns'
143−groupedDecls (FunctionBody ns) x | Just ns' <- isPragma x = ns `intersects` ns'
144−groupedDecls x (DataDeclaration n) | Just ns <- isPragma x = n `elem` ns
145−groupedDecls (DataDeclaration n) x
146− | Just ns <- isPragma x =
147− let f = occNameFS . rdrNameOcc in f n `elem` map f ns
148−groupedDecls x y | Just ns <- isPragma x, Just ns' <- isPragma y = ns `intersects` ns'
149−groupedDecls x (TypeSignature ns) | Just ns' <- isPragma x = ns `intersects` ns'
150−groupedDecls (TypeSignature ns) x | Just ns' <- isPragma x = ns `intersects` ns'
151−groupedDecls (PatternSignature ns) (Pattern n) = n `elem` ns
152−groupedDecls DocNext _ = True
153−groupedDecls _ DocPrev = True
154−groupedDecls _ _ = False
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
155209
156210intersects :: Ord a => [a] -> [a] -> Bool
157211intersects a b = go (sort a) (sort b)
@@ -164,13 +218,6 @@ intersects a b = go (sort a) (sort b)
164218 | x > y = go (x : xs) ys
165219 | otherwise = True
166220
167−-- | Checks if given list of declarations contain a pair which should
168−-- be separated by a blank line.
169−hasSeparatedDecls :: [LHsDecl GhcPs] -> Bool
170−hasSeparatedDecls xs = case groupDecls xs of
171− _ : _ : _ -> True
172− _ -> False
173−
174221isPragma ::
175222 HsDecl GhcPs ->
176223 Maybe [RdrName]
@@ -183,6 +230,11 @@ isPragma = \case
183230 WarningPragma n -> Just n
184231 _ -> Nothing
185232
233+-- Declarations that do not refer to names
234+
235+pattern Splice :: HsDecl GhcPs
236+pattern Splice <- SpliceD NoExtField (SpliceDecl NoExtField _ _)
237+
186238-- Declarations referring to a single name
187239
188240pattern
@@ -192,71 +244,84 @@ pattern
192244 AnnTypePragma,
193245 AnnValuePragma,
194246 Pattern,
195− DataDeclaration ::
247+ DataDeclaration,
248+ ClassDeclaration,
249+ KindSignature,
250+ FamilyDeclaration ::
196251 RdrName -> HsDecl GhcPs
197−pattern InlinePragma n <- SigD NoExt (InlineSig NoExt (L _ n) _)
198−pattern SpecializePragma n <- SigD NoExt (SpecSig NoExt (L _ n) _ _)
199−pattern SCCPragma n <- SigD NoExt (SCCFunSig NoExt _ (L _ n) _)
200−pattern AnnTypePragma n <- AnnD NoExt (HsAnnotation NoExt _ (TypeAnnProvenance (L _ n)) _)
201−pattern AnnValuePragma n <- AnnD NoExt (HsAnnotation NoExt _ (ValueAnnProvenance (L _ n)) _)
202−pattern Pattern n <- ValD NoExt (PatSynBind NoExt (PSB _ (L _ n) _ _ _))
203−pattern DataDeclaration n <- TyClD NoExt (DataDecl NoExt (L _ n) _ _ _)
252+pattern InlinePragma n <- SigD NoExtField (InlineSig NoExtField (L _ n) _)
253+pattern SpecializePragma n <- SigD NoExtField (SpecSig NoExtField (L _ n) _ _)
254+pattern SCCPragma n <- SigD NoExtField (SCCFunSig NoExtField _ (L _ n) _)
255+pattern AnnTypePragma n <- AnnD NoExtField (HsAnnotation NoExtField _ (TypeAnnProvenance (L _ n)) _)
256+pattern AnnValuePragma n <- AnnD NoExtField (HsAnnotation NoExtField _ (ValueAnnProvenance (L _ n)) _)
257+pattern Pattern n <- ValD NoExtField (PatSynBind NoExtField (PSB _ (L _ n) _ _ _))
258+pattern DataDeclaration n <- TyClD NoExtField (DataDecl NoExtField (L _ n) _ _ _)
259+pattern ClassDeclaration n <- TyClD NoExtField (ClassDecl NoExtField _ (L _ n) _ _ _ _ _ _ _ _)
260+pattern KindSignature n <- KindSigD NoExtField (StandaloneKindSig NoExtField (L _ n) _)
261+pattern FamilyDeclaration n <- TyClD NoExtField (FamDecl NoExtField (FamilyDecl NoExtField _ (L _ n) _ _ _ _))
204262
205263-- Declarations which can refer to multiple names
206264
207265pattern
208266 TypeSignature,
267+ DefaultSignature,
209268 FunctionBody,
210269 PatternSignature,
211270 WarningPragma ::
212271 [RdrName] -> HsDecl GhcPs
213272pattern TypeSignature n <- (sigRdrNames -> Just n)
273+pattern DefaultSignature n <- (defSigRdrNames -> Just n)
214274pattern FunctionBody n <- (funRdrNames -> Just n)
215275pattern PatternSignature n <- (patSigRdrNames -> Just n)
216276pattern WarningPragma n <- (warnSigRdrNames -> Just n)
217277
218278pattern DocNext, DocPrev :: HsDecl GhcPs
219−pattern DocNext <- (DocD NoExt (DocCommentNext _))
220−pattern DocPrev <- (DocD NoExt (DocCommentPrev _))
279+pattern DocNext <- (DocD NoExtField (DocCommentNext _))
280+pattern DocPrev <- (DocD NoExtField (DocCommentPrev _))
221281
222282sigRdrNames :: HsDecl GhcPs -> Maybe [RdrName]
223−sigRdrNames (SigD NoExt (TypeSig NoExt ns _)) = Just $ map unLoc ns
224−sigRdrNames (SigD NoExt (ClassOpSig NoExt _ ns _)) = Just $ map unLoc ns
225−sigRdrNames (SigD NoExt (PatSynSig NoExt ns _)) = Just $ map unLoc ns
283+sigRdrNames (SigD NoExtField (TypeSig NoExtField ns _)) = Just $ map unLoc ns
284+sigRdrNames (SigD NoExtField (ClassOpSig NoExtField _ ns _)) = Just $ map unLoc ns
285+sigRdrNames (SigD NoExtField (PatSynSig NoExtField ns _)) = Just $ map unLoc ns
226286sigRdrNames _ = Nothing
227287
288+defSigRdrNames :: HsDecl GhcPs -> Maybe [RdrName]
289+defSigRdrNames (SigD NoExtField (ClassOpSig NoExtField True ns _)) = Just $ map unLoc ns
290+defSigRdrNames _ = Nothing
291+
228292funRdrNames :: HsDecl GhcPs -> Maybe [RdrName]
229−funRdrNames (ValD NoExt (FunBind NoExt (L _ n) _ _ _)) = Just [n]
230−funRdrNames (ValD NoExt (PatBind NoExt n _ _)) = Just $ patBindNames n
293+funRdrNames (ValD NoExtField (FunBind NoExtField (L _ n) _ _ _)) = Just [n]
294+funRdrNames (ValD NoExtField (PatBind NoExtField (L _ n) _ _)) = Just $ patBindNames n
231295funRdrNames _ = Nothing
232296
233297patSigRdrNames :: HsDecl GhcPs -> Maybe [RdrName]
234−patSigRdrNames (SigD NoExt (PatSynSig NoExt ns _)) = Just $ map unLoc ns
298+patSigRdrNames (SigD NoExtField (PatSynSig NoExtField ns _)) = Just $ map unLoc ns
235299patSigRdrNames _ = Nothing
236300
237301warnSigRdrNames :: HsDecl GhcPs -> Maybe [RdrName]
238−warnSigRdrNames (WarningD NoExt (Warnings NoExt _ ws)) = Just $ flip concatMap ws $ \case
239− L _ (Warning NoExt ns _) -> map unLoc ns
240− L _ (XWarnDecl NoExt) -> []
302+warnSigRdrNames (WarningD NoExtField (Warnings NoExtField _ ws)) = Just $
303+ flip concatMap ws $ \case
304+ L _ (Warning NoExtField ns _) -> map unLoc ns
305+ L _ (XWarnDecl x) -> noExtCon x
241306warnSigRdrNames _ = Nothing
242307
243308patBindNames :: Pat GhcPs -> [RdrName]
244−patBindNames (TuplePat NoExt ps _) = concatMap (patBindNames . unLoc) ps
245−patBindNames (VarPat NoExt (L _ n)) = [n]
246−patBindNames (WildPat NoExt) = []
247−patBindNames (LazyPat NoExt p) = patBindNames p
248−patBindNames (BangPat NoExt p) = patBindNames p
249−patBindNames (ParPat NoExt p) = patBindNames p
250−patBindNames (ListPat NoExt ps) = concatMap (patBindNames . unLoc) ps
251−patBindNames (AsPat NoExt (L _ n) p) = n : patBindNames p
252−patBindNames (SumPat NoExt p _ _) = patBindNames p
253−patBindNames (ViewPat NoExt _ p) = patBindNames p
254−patBindNames (SplicePat NoExt _) = []
255−patBindNames (LitPat NoExt _) = []
256−patBindNames (SigPat _ p _) = patBindNames p
257−patBindNames (NPat NoExt _ _ _) = []
258−patBindNames (NPlusKPat NoExt (L _ n) _ _ _ _) = [n]
309+patBindNames (TuplePat NoExtField ps _) = concatMap (patBindNames . unLoc) ps
310+patBindNames (VarPat NoExtField (L _ n)) = [n]
311+patBindNames (WildPat NoExtField) = []
312+patBindNames (LazyPat NoExtField (L _ p)) = patBindNames p
313+patBindNames (BangPat NoExtField (L _ p)) = patBindNames p
314+patBindNames (ParPat NoExtField (L _ p)) = patBindNames p
315+patBindNames (ListPat NoExtField ps) = concatMap (patBindNames . unLoc) ps
316+patBindNames (AsPat NoExtField (L _ n) (L _ p)) = n : patBindNames p
317+patBindNames (SumPat NoExtField (L _ p) _ _) = patBindNames p
318+patBindNames (ViewPat NoExtField _ (L _ p)) = patBindNames p
319+patBindNames (SplicePat NoExtField _) = []
320+patBindNames (LitPat NoExtField _) = []
321+patBindNames (SigPat _ (L _ p) _) = patBindNames p
322+patBindNames (NPat NoExtField _ _ _) = []
323+patBindNames (NPlusKPat NoExtField (L _ n) _ _ _ _) = [n]
259324patBindNames (ConPatIn _ d) = concatMap (patBindNames . unLoc) (hsConPatArgs d)
260−patBindNames (ConPatOut _ _ _ _ _ _ _) = notImplemented "ConPatOut" -- created by renamer
261−patBindNames (CoPat NoExt _ p _) = patBindNames p
262−patBindNames (XPat p) = patBindNames (unLoc p)
325+patBindNames ConPatOut {} = notImplemented "ConPatOut" -- created by renamer
326+patBindNames (CoPat NoExtField _ p _) = patBindNames p
327+patBindNames (XPat x) = noExtCon x
src/Ormolu/Printer/Meat/Declaration.hs-boot+2−3
@@ -1,6 +1,6 @@
11module Ormolu.Printer.Meat.Declaration
22 ( p_hsDecls,
3− hasSeparatedDecls,
3+ p_hsDeclsRespectGrouping,
44 )
55where
66
@@ -9,5 +9,4 @@ import Ormolu.Printer.Combinators
99import Ormolu.Printer.Meat.Common
1010
1111p_hsDecls :: FamilyStyle -> [LHsDecl GhcPs] -> R ()
12−
13−hasSeparatedDecls :: [LHsDecl GhcPs] -> Bool
12+p_hsDeclsRespectGrouping :: FamilyStyle -> [LHsDecl GhcPs] -> R ()
src/Ormolu/Printer/Meat/Declaration/Annotation.hs+2−3
@@ -10,15 +10,14 @@ import GHC
1010import Ormolu.Printer.Combinators
1111import Ormolu.Printer.Meat.Common
1212import Ormolu.Printer.Meat.Declaration.Value
13−import Ormolu.Utils
1413
1514p_annDecl :: AnnDecl GhcPs -> R ()
1615p_annDecl = \case
17− HsAnnotation NoExt _ annProv expr -> pragma "ANN" . inci $ do
16+ HsAnnotation NoExtField _ annProv expr -> pragma "ANN" . inci $ do
1817 p_annProv annProv
1918 breakpoint
2019 located expr p_hsExpr
21− XAnnDecl {} -> notImplemented "XAnnDecl"
20+ XAnnDecl x -> noExtCon x
2221
2322p_annProv :: AnnProvenance (IdP GhcPs) -> R ()
2423p_annProv = \case
src/Ormolu/Printer/Meat/Declaration/Class.hs+26−38
@@ -12,15 +12,12 @@ import Class
1212import Control.Arrow
1313import Control.Monad
1414import Data.Foldable
15−import Data.List (sortBy)
16−import Data.Ord (comparing)
15+import Data.List (sortOn)
1716import GHC
1817import Ormolu.Printer.Combinators
1918import Ormolu.Printer.Meat.Common
2019import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration
2120import Ormolu.Printer.Meat.Type
22−import Ormolu.Utils
23−import RdrName (RdrName (..))
2421
2522p_classDecl ::
2623 LHsContext GhcPs ->
@@ -31,49 +28,47 @@ p_classDecl ::
3128 [LSig GhcPs] ->
3229 LHsBinds GhcPs ->
3330 [LFamilyDecl GhcPs] ->
34− [LTyFamDefltEqn GhcPs] ->
31+ [LTyFamDefltDecl GhcPs] ->
3532 [LDocDecl] ->
3633 R ()
37−p_classDecl ctx name tvars fixity fdeps csigs cdefs cats catdefs cdocs = do
38− let HsQTvs {..} = tvars
39− variableSpans = getLoc <$> hsq_explicit
34+p_classDecl ctx name HsQTvs {..} fixity fdeps csigs cdefs cats catdefs cdocs = do
35+ let variableSpans = getLoc <$> hsq_explicit
4036 signatureSpans = getLoc name : variableSpans
4137 dependencySpans = getLoc <$> fdeps
4238 combinedSpans = getLoc ctx : (signatureSpans ++ dependencySpans)
39+ -- GHC's AST does not necessarily store each kind of element in source
40+ -- location order. This happens because different declarations are stored
41+ -- in different lists. Consequently, to get all the declarations in proper
42+ -- order, they need to be manually sorted.
43+ sigs = (getLoc &&& fmap (SigD NoExtField)) <$> csigs
44+ vals = (getLoc &&& fmap (ValD NoExtField)) <$> toList cdefs
45+ tyFams = (getLoc &&& fmap (TyClD NoExtField . FamDecl NoExtField)) <$> cats
46+ docs = (getLoc &&& fmap (DocD NoExtField)) <$> cdocs
47+ tyFamDefs =
48+ ( getLoc &&& fmap (InstD NoExtField . TyFamInstD NoExtField)
49+ )
50+ <$> catdefs
51+ allDecls =
52+ snd <$> sortOn fst (sigs <> vals <> tyFams <> tyFamDefs <> docs)
4353 txt "class"
4454 switchLayout combinedSpans $ do
4555 breakpoint
4656 inci $ do
4757 p_classContext ctx
48− switchLayout signatureSpans $ do
58+ switchLayout signatureSpans $
4959 p_infixDefHelper
5060 (isInfix fixity)
51− inci
61+ True
5262 (p_rdrName name)
5363 (located' p_hsTyVarBndr <$> hsq_explicit)
5464 inci (p_classFundeps fdeps)
55− -- NOTE GHC's AST does not necessarily store each kind of element in
56− -- source location order. This happens because different declarations are
57− -- stored in different lists. Consequently, to get all the declarations in
58− -- proper order, they need to be manually sorted.
59− let sigs = (getLoc &&& fmap (SigD NoExt)) <$> csigs
60− vals = (getLoc &&& fmap (ValD NoExt)) <$> toList cdefs
61− tyFams = (getLoc &&& fmap (TyClD NoExt . FamDecl NoExt)) <$> cats
62− docs = (getLoc &&& fmap (DocD NoExt)) <$> cdocs
63− tyFamDefs =
64− ( getLoc &&& fmap (InstD NoExt . TyFamInstD NoExt . defltEqnToInstDecl)
65− )
66− <$> catdefs
67− allDecls =
68− snd <$> sortBy (comparing fst) (sigs <> vals <> tyFams <> tyFamDefs <> docs)
65+ unless (null allDecls) $ do
66+ breakpoint
67+ txt "where"
6968 unless (null allDecls) $ do
70− space
71− txt "where"
7269 breakpoint -- Ensure whitespace is added after where clause.
73− -- Add newline before first declaration if the body contains separate
74− -- declarations.
75− when (hasSeparatedDecls allDecls) breakpoint'
76− inci (p_hsDecls Associated allDecls)
70+ inci (p_hsDeclsRespectGrouping Associated allDecls)
71+p_classDecl _ _ (XLHsQTyVars c) _ _ _ _ _ _ _ = noExtCon c
7772
7873p_classContext :: LHsContext GhcPs -> R ()
7974p_classContext ctx = unless (null (unLoc ctx)) $ do
@@ -87,7 +82,7 @@ p_classFundeps fdeps = unless (null fdeps) $ do
8782 breakpoint
8883 txt "|"
8984 space
90− sitcc $ sep (comma >> breakpoint) (sitcc . located' p_funDep) fdeps
85+ inci $ sep commaDel (sitcc . located' p_funDep) fdeps
9186
9287p_funDep :: FunDep (Located RdrName) -> R ()
9388p_funDep (before, after) = do
@@ -100,13 +95,6 @@ p_funDep (before, after) = do
10095----------------------------------------------------------------------------
10196-- Helpers
10297
103−defltEqnToInstDecl :: TyFamDefltEqn GhcPs -> TyFamInstDecl GhcPs
104−defltEqnToInstDecl FamEqn {..} = TyFamInstDecl {..}
105− where
106− eqn = FamEqn {feqn_pats = map HsValArg (tyVarsToTypes feqn_pats), ..}
107− tfid_eqn = HsIB {hsib_ext = NoExt, hsib_body = eqn}
108−defltEqnToInstDecl XFamEqn {} = notImplemented "XFamEqn"
109−
11098isInfix :: LexicalFixity -> Bool
11199isInfix = \case
112100 Infix -> True
src/Ormolu/Printer/Meat/Declaration/Data.hs+74−47
@@ -9,15 +9,12 @@ module Ormolu.Printer.Meat.Declaration.Data
99where
1010
1111import Control.Monad
12−import Data.Maybe (isJust)
13−import Data.Maybe (maybeToList)
12+import Data.Maybe (isJust, maybeToList)
1413import GHC
1514import Ormolu.Printer.Combinators
1615import Ormolu.Printer.Meat.Common
1716import Ormolu.Printer.Meat.Type
1817import Ormolu.Utils
19−import RdrName (RdrName (..))
20−import SrcLoc (Located)
2118
2219p_dataDecl ::
2320 -- | Whether to format as data family
@@ -38,12 +35,13 @@ p_dataDecl style name tpats fixity HsDataDefn {..} = do
3835 txt $ case style of
3936 Associated -> mempty
4037 Free -> " instance"
41− switchLayout (getLoc name : fmap getLoc tpats) $ do
38+ let constructorSpans = getLoc name : fmap getLoc tpats
39+ switchLayout constructorSpans $ do
4240 breakpoint
4341 inci $
4442 p_infixDefHelper
4543 (isInfix fixity)
46− inci
44+ True
4745 (p_rdrName name)
4846 (located' p_hsType <$> tpats)
4947 case dd_kindSig of
@@ -56,29 +54,42 @@ p_dataDecl style name tpats fixity HsDataDefn {..} = do
5654 let gadt = isJust dd_kindSig || any (isGadt . unLoc) dd_cons
5755 unless (null dd_cons) $
5856 if gadt
59− then do
60− space
61− txt "where"
62− breakpoint
63− inci $ sepSemi (located' p_conDecl) dd_cons
64− else switchLayout (getLoc name : (getLoc <$> dd_cons))
65− $ inci
66− $ do
57+ then inci $ do
58+ switchLayout constructorSpans $ do
6759 breakpoint
68− txt "="
69− space
70− let s =
71− vlayout
72− (space >> txt "|" >> space)
73− (newline >> txt "|" >> space)
74− sep s (sitcc . located' p_conDecl) dd_cons
60+ txt "where"
61+ breakpoint
62+ sepSemi (located' (p_conDecl False)) dd_cons
63+ else switchLayout (getLoc name : (getLoc <$> dd_cons)) . inci $ do
64+ let singleConstRec = isSingleConstRec dd_cons
65+ if singleConstRec
66+ then space
67+ else
68+ if hasHaddocks dd_cons
69+ then newline
70+ else breakpoint
71+ equals
72+ space
73+ layout <- getLayout
74+ let s =
75+ if layout == MultiLine || hasHaddocks dd_cons
76+ then newline >> txt "|" >> space
77+ else space >> txt "|" >> space
78+ sitcc' =
79+ if singleConstRec
80+ then id
81+ else sitcc
82+ sep s (sitcc' . located' (p_conDecl singleConstRec)) dd_cons
7583 unless (null $ unLoc dd_derivs) breakpoint
76− inci . located dd_derivs $ \xs -> do
84+ inci . located dd_derivs $ \xs ->
7785 sep newline (located' p_hsDerivingClause) xs
78−p_dataDecl _ _ _ _ (XHsDataDefn NoExt) = notImplemented "XHsDataDefn"
86+p_dataDecl _ _ _ _ (XHsDataDefn x) = noExtCon x
7987
80−p_conDecl :: ConDecl GhcPs -> R ()
81−p_conDecl = \case
88+p_conDecl ::
89+ Bool ->
90+ ConDecl GhcPs ->
91+ R ()
92+p_conDecl singleConstRec = \case
8293 ConDeclGADT {..} -> do
8394 mapM_ (p_hsDocString Pipe True) con_doc
8495 let conDeclSpn =
@@ -93,19 +104,19 @@ p_conDecl = \case
93104 (c : cs) -> do
94105 p_rdrName c
95106 unless (null cs) . inci $ do
96− comma
97− breakpoint
98− sitcc $ sep (comma >> breakpoint) p_rdrName cs
99− space
107+ commaDel
108+ sep commaDel p_rdrName cs
100109 inci $ do
110+ space
101111 txt "::"
102112 let interArgBreak =
103113 if hasDocStrings (unLoc con_res_ty)
104114 then newline
105115 else breakpoint
106116 interArgBreak
107− p_forallBndrs p_hsTyVarBndr (hsq_explicit con_qvars)
108− unless (null $ hsq_explicit con_qvars) interArgBreak
117+ when (unLoc con_forall) $ do
118+ p_forallBndrs ForallInvis p_hsTyVarBndr (hsq_explicit con_qvars)
119+ interArgBreak
109120 forM_ con_mb_cxt p_lhsContext
110121 case con_args of
111122 PrefixCon xs -> do
@@ -124,24 +135,27 @@ p_conDecl = \case
124135 p_hsType (unLoc con_res_ty)
125136 ConDeclH98 {..} -> do
126137 mapM_ (p_hsDocString Pipe True) con_doc
127− let conDeclSpn =
128− [getLoc con_name]
138+ let conDeclWithContextSpn =
139+ [getLoc con_forall]
129140 <> fmap getLoc con_ex_tvs
130141 <> maybeToList (fmap getLoc con_mb_cxt)
131− <> conArgsSpans con_args
132− switchLayout conDeclSpn $ do
133− p_forallBndrs p_hsTyVarBndr con_ex_tvs
134− unless (null con_ex_tvs) breakpoint
142+ <> conDeclSpn
143+ conDeclSpn =
144+ getLoc con_name : conArgsSpans con_args
145+ switchLayout conDeclWithContextSpn $ do
146+ when (unLoc con_forall) $ do
147+ p_forallBndrs ForallInvis p_hsTyVarBndr con_ex_tvs
148+ breakpoint
135149 forM_ con_mb_cxt p_lhsContext
136− case con_args of
150+ switchLayout conDeclSpn $ case con_args of
137151 PrefixCon xs -> do
138152 p_rdrName con_name
139153 unless (null xs) breakpoint
140− inci . sitcc $ sep breakpoint (sitcc . located' p_hsType) xs
154+ inci . sitcc $ sep breakpoint (sitcc . located' p_hsTypePostDoc) xs
141155 RecCon l -> do
142156 p_rdrName con_name
143157 breakpoint
144− inci $ located l p_conDeclFields
158+ inciIf (not singleConstRec) (located l p_conDeclFields)
145159 InfixCon x y -> do
146160 located x p_hsType
147161 breakpoint
@@ -149,7 +163,7 @@ p_conDecl = \case
149163 p_rdrName con_name
150164 space
151165 located y p_hsType
152− XConDecl NoExt -> notImplemented "XConDecl"
166+ XConDecl x -> noExtCon x
153167
154168conArgsSpans :: HsConDeclDetails GhcPs -> [SrcSpan]
155169conArgsSpans = \case
@@ -163,7 +177,7 @@ conArgsSpans = \case
163177conTyVarsSpans :: LHsQTyVars GhcPs -> [SrcSpan]
164178conTyVarsSpans = \case
165179 HsQTvs {..} -> getLoc <$> hsq_explicit
166− XLHsQTyVars NoExt -> []
180+ XLHsQTyVars x -> noExtCon x
167181
168182p_lhsContext ::
169183 LHsContext GhcPs ->
@@ -190,9 +204,9 @@ p_hsDerivingClause HsDerivingClause {..} = do
190204 let derivingWhat = located deriv_clause_tys $ \case
191205 [] -> txt "()"
192206 xs ->
193− parens N . sitcc $
207+ parens N $
194208 sep
195− (comma >> breakpoint)
209+ commaDel
196210 (sitcc . located' p_hsType . hsib_body)
197211 xs
198212 space
@@ -221,9 +235,9 @@ p_hsDerivingClause HsDerivingClause {..} = do
221235 txt "via"
222236 space
223237 located hsib_body p_hsType
224− ViaStrategy (XHsImplicitBndrs NoExt) ->
225− notImplemented "XHsImplicitBndrs"
226−p_hsDerivingClause (XHsDerivingClause NoExt) = notImplemented "XHsDerivingClause"
238+ ViaStrategy (XHsImplicitBndrs x) ->
239+ noExtCon x
240+p_hsDerivingClause (XHsDerivingClause x) = noExtCon x
227241
228242----------------------------------------------------------------------------
229243-- Helpers
@@ -232,3 +246,16 @@ isInfix :: LexicalFixity -> Bool
232246isInfix = \case
233247 Infix -> True
234248 Prefix -> False
249+
250+isSingleConstRec :: [LConDecl GhcPs] -> Bool
251+isSingleConstRec [(L _ ConDeclH98 {..})] =
252+ case con_args of
253+ RecCon _ -> True
254+ _ -> False
255+isSingleConstRec _ = False
256+
257+hasHaddocks :: [LConDecl GhcPs] -> Bool
258+hasHaddocks = any (f . unLoc)
259+ where
260+ f ConDeclH98 {..} = isJust con_doc
261+ f _ = False
src/Ormolu/Printer/Meat/Declaration/Default.hs+4−5
@@ -9,13 +9,12 @@ where
99import GHC
1010import Ormolu.Printer.Combinators
1111import Ormolu.Printer.Meat.Type
12−import Ormolu.Utils
1312
1413p_defaultDecl :: DefaultDecl GhcPs -> R ()
1514p_defaultDecl = \case
16− DefaultDecl NoExt ts -> do
15+ DefaultDecl NoExtField ts -> do
1716 txt "default"
1817 breakpoint
19− inci . parens N . sitcc $
20− sep (comma >> breakpoint) (sitcc . located' p_hsType) ts
21− XDefaultDecl {} -> notImplemented "XDefaultDecl"
18+ inci . parens N $
19+ sep commaDel (sitcc . located' p_hsType) ts
20+ XDefaultDecl x -> noExtCon x
src/Ormolu/Printer/Meat/Declaration/Foreign.hs+2−3
@@ -15,7 +15,6 @@ import GHC
1515import Ormolu.Printer.Combinators
1616import Ormolu.Printer.Meat.Common
1717import Ormolu.Printer.Meat.Declaration.Signature
18−import Ormolu.Utils
1918
2019p_foreignDecl :: ForeignDecl GhcPs -> R ()
2120p_foreignDecl = \case
@@ -25,7 +24,7 @@ p_foreignDecl = \case
2524 fd@ForeignExport {fd_fe} -> do
2625 p_foreignExport fd_fe
2726 p_foreignTypeSig fd
28− XForeignDecl {} -> notImplemented "XForeignDecl"
27+ XForeignDecl x -> noExtCon x
2928
3029-- | Printer for the last part of an import\/export, which is function name
3130-- and type signature.
@@ -39,7 +38,7 @@ p_foreignTypeSig fd = do
3938 ]
4039 $ do
4140 p_rdrName (fd_name fd)
42− p_typeAscription (HsWC NoExt (fd_sig_ty fd))
41+ p_typeAscription (HsWC NoExtField (fd_sig_ty fd))
4342
4443-- | Printer for 'ForeignImport'.
4544--
src/Ormolu/Printer/Meat/Declaration/Instance.hs+21−27
@@ -15,8 +15,7 @@ import BasicTypes
1515import Control.Arrow
1616import Control.Monad
1717import Data.Foldable
18−import Data.List (sortBy)
19−import Data.Ord (comparing)
18+import Data.List (sortOn)
2019import GHC
2120import Ormolu.Printer.Combinators
2221import Ormolu.Printer.Meat.Common
@@ -33,13 +32,11 @@ p_standaloneDerivDecl DerivDecl {..} = do
3332 txt "instance"
3433 breakpoint
3534 match_overlap_mode deriv_overlap_mode breakpoint
36− if toIndent
37− then inci typesAfterInstance
38− else typesAfterInstance
35+ inciIf toIndent typesAfterInstance
3936 txt "deriving"
4037 space
4138 case deriv_strategy of
42− Nothing -> do
39+ Nothing ->
4340 instTypes False
4441 Just (L _ a) -> case a of
4542 StockStrategy -> do
@@ -57,8 +54,8 @@ p_standaloneDerivDecl DerivDecl {..} = do
5754 inci (located hsib_body p_hsType)
5855 breakpoint
5956 instTypes True
60− ViaStrategy (XHsImplicitBndrs NoExt) ->
61− notImplemented "XHsImplicitBndrs"
57+ ViaStrategy (XHsImplicitBndrs x) ->
58+ noExtCon x
6259p_standaloneDerivDecl (XDerivDecl _) = notImplemented "XDerivDecl"
6360
6461p_clsInstDecl :: ClsInstDecl GhcPs -> R ()
@@ -71,19 +68,18 @@ p_clsInstDecl = \case
7168 -- location order. This happens because different declarations are stored in
7269 -- different lists. Consequently, to get all the declarations in proper
7370 -- order, they need to be manually sorted.
74− let sigs = (getLoc &&& fmap (SigD NoExt)) <$> cid_sigs
75− vals = (getLoc &&& fmap (ValD NoExt)) <$> toList cid_binds
71+ let sigs = (getLoc &&& fmap (SigD NoExtField)) <$> cid_sigs
72+ vals = (getLoc &&& fmap (ValD NoExtField)) <$> toList cid_binds
7673 tyFamInsts =
77− ( getLoc &&& fmap (InstD NoExt . TyFamInstD NoExt)
74+ ( getLoc &&& fmap (InstD NoExtField . TyFamInstD NoExtField)
7875 )
7976 <$> cid_tyfam_insts
8077 dataFamInsts =
81− ( getLoc &&& fmap (InstD NoExt . DataFamInstD NoExt)
78+ ( getLoc &&& fmap (InstD NoExtField . DataFamInstD NoExtField)
8279 )
8380 <$> cid_datafam_insts
8481 allDecls =
85− snd
86− <$> sortBy (comparing fst) (sigs <> vals <> tyFamInsts <> dataFamInsts)
82+ snd <$> sortOn fst (sigs <> vals <> tyFamInsts <> dataFamInsts)
8783 located hsib_body $ \x -> do
8884 breakpoint
8985 inci $ do
@@ -92,16 +88,12 @@ p_clsInstDecl = \case
9288 unless (null allDecls) $ do
9389 breakpoint
9490 txt "where"
95− unless (null allDecls) $ do
96− inci $ do
97− -- Ensure whitespace is added after where clause.
98− breakpoint
99− -- Add newline before first declaration if the body contains separate
100− -- declarations
101− when (hasSeparatedDecls allDecls) breakpoint'
102− dontUseBraces $ p_hsDecls Associated allDecls
103− XHsImplicitBndrs NoExt -> notImplemented "XHsImplicitBndrs"
104− XClsInstDecl NoExt -> notImplemented "XClsInstDecl"
91+ unless (null allDecls) . inci $ do
92+ -- Ensure whitespace is added after where clause.
93+ breakpoint
94+ dontUseBraces $ p_hsDeclsRespectGrouping Associated allDecls
95+ XHsImplicitBndrs x -> noExtCon x
96+ XClsInstDecl x -> noExtCon x
10597
10698p_tyFamInstDecl :: FamilyStyle -> TyFamInstDecl GhcPs -> R ()
10799p_tyFamInstDecl style = \case
@@ -114,10 +106,12 @@ p_tyFamInstDecl style = \case
114106
115107p_dataFamInstDecl :: FamilyStyle -> DataFamInstDecl GhcPs -> R ()
116108p_dataFamInstDecl style = \case
117− DataFamInstDecl {..} -> do
118− let HsIB {..} = dfid_eqn
119− FamEqn {..} = hsib_body
109+ DataFamInstDecl {dfid_eqn = HsIB {hsib_body = FamEqn {..}}} ->
120110 p_dataDecl style feqn_tycon (map typeArgToType feqn_pats) feqn_fixity feqn_rhs
111+ DataFamInstDecl {dfid_eqn = HsIB {hsib_body = XFamEqn {}}} ->
112+ notImplemented "XFamEqn"
113+ DataFamInstDecl {dfid_eqn = XHsImplicitBndrs {}} ->
114+ notImplemented "XHsImplicitBndrs"
121115
122116match_overlap_mode :: Maybe (Located OverlapMode) -> R () -> R ()
123117match_overlap_mode overlap_mode layoutStrategy =
src/Ormolu/Printer/Meat/Declaration/RoleAnnotation.hs+2−6
@@ -1,6 +1,5 @@
11{-# LANGUAGE LambdaCase #-}
22{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE RecordWildCards #-}
43{-# LANGUAGE TypeFamilies #-}
54
65-- | Rendering of Role annotation declarations.
@@ -13,14 +12,11 @@ import CoAxiom
1312import GHC
1413import Ormolu.Printer.Combinators
1514import Ormolu.Printer.Meat.Common
16−import Ormolu.Utils
17−import RdrName (RdrName (..))
18−import SrcLoc (Located)
1915
2016p_roleAnnot :: RoleAnnotDecl GhcPs -> R ()
2117p_roleAnnot = \case
22− RoleAnnotDecl NoExt l_name anns -> p_roleAnnot' l_name anns
23− XRoleAnnotDecl _ -> notImplemented "XRoleAnnotDecl"
18+ RoleAnnotDecl NoExtField l_name anns -> p_roleAnnot' l_name anns
19+ XRoleAnnotDecl x -> noExtCon x
2420
2521p_roleAnnot' :: Located RdrName -> [Located (Maybe Role)] -> R ()
2622p_roleAnnot' l_name anns = do
src/Ormolu/Printer/Meat/Declaration/Rule.hs+21−16
@@ -1,6 +1,5 @@
11{-# LANGUAGE LambdaCase #-}
22{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE RecordWildCards #-}
43
54module Ormolu.Printer.Meat.Declaration.Rule
65 ( p_ruleDecls,
@@ -8,49 +7,55 @@ module Ormolu.Printer.Meat.Declaration.Rule
87where
98
109import BasicTypes
11−import Data.Maybe (fromMaybe)
10+import Control.Monad (unless)
1211import GHC
1312import Ormolu.Printer.Combinators
1413import Ormolu.Printer.Meat.Common
1514import Ormolu.Printer.Meat.Declaration.Signature
1615import Ormolu.Printer.Meat.Declaration.Value
1716import Ormolu.Printer.Meat.Type
18−import Ormolu.Utils
1917
2018p_ruleDecls :: RuleDecls GhcPs -> R ()
2119p_ruleDecls = \case
22− HsRules NoExt _ xs ->
23− pragma "RULES" . sitcc $
20+ HsRules NoExtField _ xs ->
21+ pragma "RULES" $
2422 sep breakpoint (sitcc . located' p_ruleDecl) xs
25− XRuleDecls NoExt -> notImplemented "XRuleDecls"
23+ XRuleDecls x -> noExtCon x
2624
2725p_ruleDecl :: RuleDecl GhcPs -> R ()
2826p_ruleDecl = \case
29− HsRule NoExt ruleName activation tyvars ruleBndrs lhs rhs -> do
27+ HsRule NoExtField ruleName activation tyvars ruleBndrs lhs rhs -> do
3028 located ruleName p_ruleName
3129 space
3230 p_activation activation
3331 space
34− p_forallBndrs p_hsTyVarBndr (fromMaybe [] tyvars)
35− space
36− p_forallBndrs p_ruleBndr ruleBndrs
32+ case tyvars of
33+ Nothing -> return ()
34+ Just xs -> do
35+ p_forallBndrs ForallInvis p_hsTyVarBndr xs
36+ space
37+ -- It appears that there is no way to tell if there was an empty forall
38+ -- in the input or no forall at all. We do not want to add redundant
39+ -- foralls, so let's just skip the empty ones.
40+ unless (null ruleBndrs) $
41+ p_forallBndrs ForallInvis p_ruleBndr ruleBndrs
3742 breakpoint
3843 inci $ do
3944 located lhs p_hsExpr
4045 space
41− txt "="
46+ equals
4247 inci $ do
4348 breakpoint
4449 located rhs p_hsExpr
45− XRuleDecl NoExt -> notImplemented "XRuleDecl"
50+ XRuleDecl x -> noExtCon x
4651
4752p_ruleName :: (SourceText, RuleName) -> R ()
48−p_ruleName (_, name) = atom $ HsString NoSourceText name
53+p_ruleName (_, name) = atom $ (HsString NoSourceText name :: HsLit GhcPs)
4954
5055p_ruleBndr :: RuleBndr GhcPs -> R ()
5156p_ruleBndr = \case
52− RuleBndr NoExt x -> p_rdrName x
53− RuleBndrSig NoExt x hswc -> parens N $ do
57+ RuleBndr NoExtField x -> p_rdrName x
58+ RuleBndrSig NoExtField x hswc -> parens N $ do
5459 p_rdrName x
5560 p_typeAscription hswc
56− XRuleBndr NoExt -> notImplemented "XRuleBndr"
61+ XRuleBndr x -> noExtCon x
src/Ormolu/Printer/Meat/Declaration/Signature.hs+49−33
@@ -7,13 +7,13 @@ module Ormolu.Printer.Meat.Declaration.Signature
77 ( p_sigDecl,
88 p_typeAscription,
99 p_activation,
10+ p_standaloneKindSig,
1011 )
1112where
1213
1314import BasicTypes
1415import BooleanFormula
1516import Control.Monad
16−import Data.Bool (bool)
1717import GHC
1818import Ormolu.Printer.Combinators
1919import Ormolu.Printer.Meat.Common
@@ -22,16 +22,16 @@ import Ormolu.Utils
2222
2323p_sigDecl :: Sig GhcPs -> R ()
2424p_sigDecl = \case
25− TypeSig NoExt names hswc -> p_typeSig True names hswc
26− PatSynSig NoExt names hsib -> p_patSynSig names hsib
27− ClassOpSig NoExt def names hsib -> p_classOpSig def names hsib
28− FixSig NoExt sig -> p_fixSig sig
29− InlineSig NoExt name inlinePragma -> p_inlineSig name inlinePragma
30− SpecSig NoExt name ts inlinePragma -> p_specSig name ts inlinePragma
31− SpecInstSig NoExt _ hsib -> p_specInstSig hsib
32− MinimalSig NoExt _ booleanFormula -> p_minimalSig booleanFormula
33− CompleteMatchSig NoExt _sourceText cs ty -> p_completeSig cs ty
34− SCCFunSig NoExt _ name literal -> p_sccSig name literal
25+ TypeSig NoExtField names hswc -> p_typeSig True names hswc
26+ PatSynSig NoExtField names hsib -> p_patSynSig names hsib
27+ ClassOpSig NoExtField def names hsib -> p_classOpSig def names hsib
28+ FixSig NoExtField sig -> p_fixSig sig
29+ InlineSig NoExtField name inlinePragma -> p_inlineSig name inlinePragma
30+ SpecSig NoExtField name ts inlinePragma -> p_specSig name ts inlinePragma
31+ SpecInstSig NoExtField _ hsib -> p_specInstSig hsib
32+ MinimalSig NoExtField _ booleanFormula -> p_minimalSig booleanFormula
33+ CompleteMatchSig NoExtField _sourceText cs ty -> p_completeSig cs ty
34+ SCCFunSig NoExtField _ name literal -> p_sccSig name literal
3535 _ -> notImplemented "certain types of signature declarations"
3636
3737p_typeSig ::
@@ -47,25 +47,23 @@ p_typeSig indentTail (n : ns) hswc = do
4747 p_rdrName n
4848 if null ns
4949 then p_typeAscription hswc
50− else bool id inci indentTail $ do
51− comma
52− breakpoint
53− sep (comma >> breakpoint) p_rdrName ns
50+ else inciIf indentTail $ do
51+ commaDel
52+ sep commaDel p_rdrName ns
5453 p_typeAscription hswc
5554
5655p_typeAscription ::
5756 LHsSigWcType GhcPs ->
5857 R ()
59−p_typeAscription HsWC {..} = do
58+p_typeAscription HsWC {..} = inci $ do
6059 space
61− inci $ do
62− txt "::"
63− let t = hsib_body hswc_body
64− if hasDocStrings (unLoc t)
65− then newline
66− else breakpoint
67− located t p_hsType
68−p_typeAscription (XHsWildCardBndrs NoExt) = notImplemented "XHsWildCardBndrs"
60+ txt "::"
61+ let t = hsib_body hswc_body
62+ if hasDocStrings (unLoc t)
63+ then newline
64+ else breakpoint
65+ located t p_hsType
66+p_typeAscription (XHsWildCardBndrs x) = noExtCon x
6967
7068p_patSynSig ::
7169 [Located RdrName] ->
@@ -73,7 +71,11 @@ p_patSynSig ::
7371 R ()
7472p_patSynSig names hsib = do
7573 txt "pattern"
76− let body = p_typeSig False names HsWC {hswc_ext = NoExt, hswc_body = hsib}
74+ let body =
75+ p_typeSig
76+ False
77+ names
78+ HsWC {hswc_ext = NoExtField, hswc_body = hsib}
7779 if length names > 1
7880 then breakpoint >> inci body
7981 else space >> body
@@ -88,13 +90,13 @@ p_classOpSig ::
8890 R ()
8991p_classOpSig def names hsib = do
9092 when def (txt "default" >> space)
91− p_typeSig True names HsWC {hswc_ext = NoExt, hswc_body = hsib}
93+ p_typeSig True names HsWC {hswc_ext = NoExtField, hswc_body = hsib}
9294
9395p_fixSig ::
9496 FixitySig GhcPs ->
9597 R ()
9698p_fixSig = \case
97− FixitySig NoExt names (Fixity _ n dir) -> do
99+ FixitySig NoExtField names (Fixity _ n dir) -> do
98100 txt $ case dir of
99101 InfixL -> "infixl"
100102 InfixR -> "infixr"
@@ -102,8 +104,8 @@ p_fixSig = \case
102104 space
103105 atom n
104106 space
105− sitcc $ sep (comma >> breakpoint) p_rdrName names
106− XFixitySig NoExt -> notImplemented "XFixitySig"
107+ sitcc $ sep commaDel p_rdrName names
108+ XFixitySig x -> noExtCon x
107109
108110p_inlineSig ::
109111 -- | Name
@@ -141,7 +143,7 @@ p_specSig name ts InlinePragma {..} = pragmaBraces $ do
141143 space
142144 txt "::"
143145 breakpoint
144− inci $ sep (comma >> breakpoint) (located' p_hsType . hsib_body) ts
146+ inci $ sep commaDel (located' p_hsType . hsib_body) ts
145147
146148p_inlineSpec :: InlineSpec -> R ()
147149p_inlineSpec = \case
@@ -185,13 +187,13 @@ p_booleanFormula = \case
185187 And xs ->
186188 sitcc $
187189 sep
188− (comma >> breakpoint)
190+ commaDel
189191 (located' p_booleanFormula)
190192 xs
191193 Or xs ->
192194 sitcc $
193195 sep
194− (breakpoint >> txt "| ")
196+ (breakpoint >> txt "|" >> space)
195197 (located' p_booleanFormula)
196198 xs
197199 Parens l -> located l (parens N . p_booleanFormula)
@@ -205,7 +207,7 @@ p_completeSig ::
205207p_completeSig cs' mty =
206208 located cs' $ \cs ->
207209 pragma "COMPLETE" . inci $ do
208− sitcc $ sep (comma >> breakpoint) p_rdrName cs
210+ sep commaDel p_rdrName cs
209211 forM_ mty $ \ty -> do
210212 space
211213 txt "::"
@@ -218,3 +220,17 @@ p_sccSig loc literal = pragma "SCC" . inci $ do
218220 forM_ literal $ \x -> do
219221 breakpoint
220222 atom x
223+
224+p_standaloneKindSig :: StandaloneKindSig GhcPs -> R ()
225+p_standaloneKindSig (StandaloneKindSig NoExtField name bndrs) = do
226+ txt "type"
227+ inci $ do
228+ space
229+ p_rdrName name
230+ space
231+ txt "::"
232+ breakpoint
233+ case bndrs of
234+ HsIB NoExtField sig -> located sig p_hsType
235+ XHsImplicitBndrs x -> noExtCon x
236+p_standaloneKindSig (XStandaloneKindSig c) = noExtCon c
src/Ormolu/Printer/Meat/Declaration/Splice.hs+2−3
@@ -8,9 +8,8 @@ where
88import GHC
99import Ormolu.Printer.Combinators
1010import Ormolu.Printer.Meat.Declaration.Value (p_hsSplice)
11−import Ormolu.Utils
1211
1312p_spliceDecl :: SpliceDecl GhcPs -> R ()
1413p_spliceDecl = \case
15− SpliceDecl NoExt splice _explicit -> located splice p_hsSplice
16− XSpliceDecl {} -> notImplemented "XSpliceDecl"
14+ SpliceDecl NoExtField splice _explicit -> located splice p_hsSplice
15+ XSpliceDecl x -> noExtCon x
src/Ormolu/Printer/Meat/Declaration/Type.hs+8−8
@@ -11,8 +11,6 @@ import GHC
1111import Ormolu.Printer.Combinators
1212import Ormolu.Printer.Meat.Common
1313import Ormolu.Printer.Meat.Type
14−import RdrName (RdrName (..))
15−import SrcLoc (Located)
1614
1715p_synDecl ::
1816 -- | Type constructor
@@ -24,17 +22,19 @@ p_synDecl ::
2422 -- | RHS of type declaration
2523 LHsType GhcPs ->
2624 R ()
27−p_synDecl name fixity tvars t = do
25+p_synDecl name fixity HsQTvs {..} t = do
2826 txt "type"
2927 space
30− let HsQTvs {..} = tvars
31− switchLayout (getLoc name : map getLoc hsq_explicit) $ do
28+ switchLayout (getLoc name : map getLoc hsq_explicit) $
3229 p_infixDefHelper
3330 (case fixity of Infix -> True; _ -> False)
34− inci
31+ True
3532 (p_rdrName name)
3633 (map (located' p_hsTyVarBndr) hsq_explicit)
3734 space
38− txt "="
39− breakpoint
35+ equals
36+ if hasDocStrings (unLoc t)
37+ then newline
38+ else breakpoint
4039 inci (located t p_hsType)
40+p_synDecl _ _ (XLHsQTyVars x) _ = noExtCon x
src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs+27−28
@@ -9,18 +9,16 @@ module Ormolu.Printer.Meat.Declaration.TypeFamily
99 )
1010where
1111
12−import BasicTypes (LexicalFixity (..))
1312import Control.Monad
14−import Data.Maybe (isJust, isNothing)
13+import Data.Maybe (isNothing)
1514import GHC
1615import Ormolu.Printer.Combinators
1716import Ormolu.Printer.Meat.Common
1817import Ormolu.Printer.Meat.Type
1918import Ormolu.Utils
20−import SrcLoc (GenLocated (..), Located)
2119
2220p_famDecl :: FamilyStyle -> FamilyDecl GhcPs -> R ()
23−p_famDecl style FamilyDecl {..} = do
21+p_famDecl style FamilyDecl {fdTyVars = HsQTvs {..}, ..} = do
2422 mmeqs <- case fdInfo of
2523 DataFamily -> Nothing <$ txt "data"
2624 OpenTypeFamily -> Nothing <$ txt "type"
@@ -28,27 +26,26 @@ p_famDecl style FamilyDecl {..} = do
2826 txt $ case style of
2927 Associated -> mempty
3028 Free -> " family"
31− let HsQTvs {..} = fdTyVars
3229 breakpoint
3330 inci $ do
34− switchLayout (getLoc fdLName : (getLoc <$> hsq_explicit)) $ do
31+ switchLayout (getLoc fdLName : (getLoc <$> hsq_explicit)) $
3532 p_infixDefHelper
3633 (isInfix fdFixity)
37− inci
34+ True
3835 (p_rdrName fdLName)
3936 (located' p_hsTyVarBndr <$> hsq_explicit)
40− let rsig = p_familyResultSigL fdResultSig
41− unless (isNothing rsig && isNothing fdInjectivityAnn) $
42− space
37+ let resultSig = p_familyResultSigL fdResultSig
38+ unless (isNothing resultSig && isNothing fdInjectivityAnn) space
4339 inci $ do
44− sequence_ rsig
45− when (isJust rsig && isJust fdInjectivityAnn) breakpoint
40+ sequence_ resultSig
41+ space
4642 forM_ fdInjectivityAnn (located' p_injectivityAnn)
4743 case mmeqs of
4844 Nothing -> return ()
4945 Just meqs -> do
50− space
51− txt "where"
46+ inci $ do
47+ breakpoint
48+ txt "where"
5249 case meqs of
5350 Nothing -> do
5451 space
@@ -56,7 +53,9 @@ p_famDecl style FamilyDecl {..} = do
5653 Just eqs -> do
5754 newline
5855 sep newline (located' (inci . p_tyFamInstEqn)) eqs
59−p_famDecl _ (XFamilyDecl NoExt) = notImplemented "XFamilyDecl"
56+p_famDecl _ FamilyDecl {fdTyVars = XLHsQTyVars {}} =
57+ notImplemented "XLHsQTyVars"
58+p_famDecl _ (XFamilyDecl x) = noExtCon x
6059
6160p_familyResultSigL ::
6261 Located (FamilyResultSig GhcPs) ->
@@ -64,17 +63,17 @@ p_familyResultSigL ::
6463p_familyResultSigL l =
6564 case l of
6665 L _ a -> case a of
67− NoSig NoExt -> Nothing
68− KindSig NoExt k -> Just $ do
66+ NoSig NoExtField -> Nothing
67+ KindSig NoExtField k -> Just $ do
6968 txt "::"
7069 breakpoint
7170 located k p_hsType
72− TyVarSig NoExt bndr -> Just $ do
73− txt "="
71+ TyVarSig NoExtField bndr -> Just $ do
72+ equals
7473 breakpoint
7574 located bndr p_hsTyVarBndr
76− XFamilyResultSig NoExt ->
77− notImplemented "XFamilyResultSig"
75+ XFamilyResultSig x ->
76+ noExtCon x
7877
7978p_injectivityAnn :: InjectivityAnn GhcPs -> R ()
8079p_injectivityAnn (InjectivityAnn a bs) = do
@@ -87,26 +86,26 @@ p_injectivityAnn (InjectivityAnn a bs) = do
8786 sep space p_rdrName bs
8887
8988p_tyFamInstEqn :: TyFamInstEqn GhcPs -> R ()
90−p_tyFamInstEqn HsIB {..} = do
91− let FamEqn {..} = hsib_body
89+p_tyFamInstEqn HsIB {hsib_body = FamEqn {..}} = do
9290 case feqn_bndrs of
9391 Nothing -> return ()
9492 Just bndrs -> do
95− p_forallBndrs p_hsTyVarBndr bndrs
93+ p_forallBndrs ForallInvis p_hsTyVarBndr bndrs
9694 breakpoint
97− (if null feqn_bndrs then id else inci) $ do
95+ inciIf (not $ null feqn_bndrs) $ do
9896 let famLhsSpn = getLoc feqn_tycon : fmap (getLoc . typeArgToType) feqn_pats
9997 switchLayout famLhsSpn $
10098 p_infixDefHelper
10199 (isInfix feqn_fixity)
102− inci
100+ True
103101 (p_rdrName feqn_tycon)
104102 (located' p_hsType . typeArgToType <$> feqn_pats)
105103 space
106− txt "="
104+ equals
107105 breakpoint
108106 inci (located feqn_rhs p_hsType)
109−p_tyFamInstEqn (XHsImplicitBndrs NoExt) = notImplemented "XHsImplicitBndrs"
107+p_tyFamInstEqn HsIB {hsib_body = XFamEqn x} = noExtCon x
108+p_tyFamInstEqn (XHsImplicitBndrs x) = noExtCon x
110109
111110----------------------------------------------------------------------------
112111-- Helpers
src/Ormolu/Printer/Meat/Declaration/Value.hs+435−357
@@ -1,6 +1,8 @@
11{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE MultiWayIf #-}
23{-# LANGUAGE OverloadedStrings #-}
34{-# LANGUAGE RecordWildCards #-}
5+{-# LANGUAGE TypeApplications #-}
46
57module Ormolu.Printer.Meat.Declaration.Value
68 ( p_valDecl,
@@ -18,23 +20,22 @@ import Ctype (is_space)
1820import Data.Bool (bool)
1921import Data.Char (isPunctuation, isSymbol)
2022import Data.Data hiding (Infix, Prefix)
23+import Data.Functor ((<&>))
2124import Data.List (intersperse, sortOn)
22−import Data.List.NonEmpty ((<|), NonEmpty ((:|)))
25+import Data.List.NonEmpty (NonEmpty (..), (<|))
2326import qualified Data.List.NonEmpty as NE
2427import Data.Text (Text)
2528import qualified Data.Text as Text
2629import GHC
27−import OccName (mkVarOcc)
30+import OccName (occNameString)
2831import Ormolu.Printer.Combinators
32+import Ormolu.Printer.Internal
2933import Ormolu.Printer.Meat.Common
3034import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration
3135import Ormolu.Printer.Meat.Declaration.Signature
3236import Ormolu.Printer.Meat.Type
3337import Ormolu.Printer.Operators
3438import Ormolu.Utils
35−import RdrName (RdrName (..))
36−import RdrName (rdrNameOcc)
37−import SrcLoc (combineSrcSpans, isOneLineSpan)
3839
3940-- | Style of a group of equations.
4041data MatchGroupStyle
@@ -60,16 +61,16 @@ data Placement
6061 -- should use it and avoid bumping one level
6162 -- of indentation
6263 Hanging
63− deriving (Eq)
64+ deriving (Eq, Show)
6465
6566p_valDecl :: HsBindLR GhcPs GhcPs -> R ()
6667p_valDecl = \case
67− FunBind NoExt funId funMatches _ _ -> p_funBind funId funMatches
68− PatBind NoExt pat grhss _ -> p_match PatternBind False NoSrcStrict [pat] grhss
68+ FunBind NoExtField funId funMatches _ _ -> p_funBind funId funMatches
69+ PatBind NoExtField pat grhss _ -> p_match PatternBind False NoSrcStrict [pat] grhss
6970 VarBind {} -> notImplemented "VarBinds" -- introduced by the type checker
7071 AbsBinds {} -> notImplemented "AbsBinds" -- introduced by the type checker
71− PatSynBind NoExt psb -> p_patSynBind psb
72− XHsBindsLR NoExt -> notImplemented "XHsBindsLR"
72+ PatSynBind NoExtField psb -> p_patSynBind psb
73+ XHsBindsLR x -> noExtCon x
7374
7475p_funBind ::
7576 Located RdrName ->
@@ -99,7 +100,7 @@ p_matchGroup' placer render style MG {..} = do
99100 Case -> id
100101 LambdaCase -> id
101102 _ -> dontUseBraces
102− -- NOTE since we are forcing braces on 'sepSemi' based on 'ob', we have to
103+ -- Since we are forcing braces on 'sepSemi' based on 'ob', we have to
103104 -- restore the brace state inside the sepsemi.
104105 ub <- bool dontUseBraces useBraces <$> canUseBraces
105106 ob $ sepSemi (located' (ub . p_Match)) (unLoc mg_alts)
@@ -113,8 +114,8 @@ p_matchGroup' placer render style MG {..} = do
113114 (matchStrictness m)
114115 m_pats
115116 m_grhss
116− p_Match _ = notImplemented "XMatch"
117−p_matchGroup' _ _ _ (XMatchGroup NoExt) = notImplemented "XMatchGroup"
117+ p_Match (XMatch x) = noExtCon x
118+p_matchGroup' _ _ _ (XMatchGroup x) = noExtCon x
118119
119120-- | Function id obtained through pattern matching on 'FunBind' should not
120121-- be used to print the actual equations because the different ‘RdrNames’
@@ -167,9 +168,9 @@ p_match' ::
167168 -- | Equations
168169 GRHSs GhcPs (Located body) ->
169170 R ()
170−p_match' placer render style isInfix strictness m_pats m_grhss = do
171− -- NOTE Normally, since patterns may be placed in a multi-line layout, it
172− -- is necessary to bump indentation for the pattern group so it's more
171+p_match' placer render style isInfix strictness m_pats GRHSs {..} = do
172+ -- Normally, since patterns may be placed in a multi-line layout, it is
173+ -- necessary to bump indentation for the pattern group so it's more
173174 -- indented than function name. This in turn means that indentation for
174175 -- the body should also be bumped. Normally this would mean that bodies
175176 -- would start with two indentation steps applied, which is ugly, so we
@@ -179,27 +180,26 @@ p_match' placer render style isInfix strictness m_pats m_grhss = do
179180 NoSrcStrict -> return ()
180181 SrcStrict -> txt "!"
181182 SrcLazy -> txt "~"
182− inci' <- case NE.nonEmpty m_pats of
183− Nothing -> id <$ case style of
184− Function name -> p_rdrName name
185− _ -> return ()
183+ indentBody <- case NE.nonEmpty m_pats of
184+ Nothing ->
185+ False <$ case style of
186+ Function name -> p_rdrName name
187+ _ -> return ()
186188 Just ne_pats -> do
187− let combinedSpans =
188− combineSrcSpans' $
189− getLoc <$> ne_pats
190− inci' =
191− if isOneLineSpan combinedSpans
192− then id
193− else inci
189+ let combinedSpans = case style of
190+ Function name -> combineSrcSpans (getLoc name) patSpans
191+ _ -> patSpans
192+ patSpans = combineSrcSpans' (getLoc <$> ne_pats)
193+ indentBody = not (isOneLineSpan combinedSpans)
194194 switchLayout [combinedSpans] $ do
195− let stdCase = sep breakpoint p_pat m_pats
195+ let stdCase = sep breakpoint (located' p_pat) m_pats
196196 case style of
197197 Function name ->
198198 p_infixDefHelper
199199 isInfix
200− inci'
200+ indentBody
201201 (p_rdrName name)
202− (p_pat <$> m_pats)
202+ (located' p_pat <$> m_pats)
203203 PatternBind -> stdCase
204204 Case -> stdCase
205205 Lambda -> do
@@ -212,41 +212,32 @@ p_match' placer render style isInfix strictness m_pats m_grhss = do
212212 when needsSpace space
213213 sitcc stdCase
214214 LambdaCase -> stdCase
215− return inci'
215+ return indentBody
216216 let -- Calculate position of end of patterns. This is useful when we decide
217217 -- about putting certain constructions in hanging positions.
218218 endOfPats = case NE.nonEmpty m_pats of
219219 Nothing -> case style of
220− Function name -> (Just . srcSpanEnd . getLoc) name
220+ Function name -> Just (getLoc name)
221221 _ -> Nothing
222− Just pats -> (Just . srcSpanEnd . getLoc . NE.last) pats
222+ Just pats -> (Just . getLoc . NE.last) pats
223223 isCase = \case
224224 Case -> True
225225 LambdaCase -> True
226226 _ -> False
227− let GRHSs {..} = m_grhss
228227 hasGuards = withGuards grhssGRHSs
229− unless (length grhssGRHSs > 1) $ do
230− case style of
231− Function _ | hasGuards -> return ()
232− Function _ -> space >> txt "="
233− PatternBind -> space >> txt "="
234− s | isCase s && hasGuards -> return ()
235− _ -> space >> txt "->"
236− let grhssSpan =
228+ grhssSpan =
237229 combineSrcSpans' $
238230 getGRHSSpan . unLoc <$> NE.fromList grhssGRHSs
239231 patGrhssSpan =
240232 maybe
241233 grhssSpan
242− (combineSrcSpans grhssSpan . srcLocSpan)
234+ (combineSrcSpans grhssSpan . srcLocSpan . srcSpanEnd)
243235 endOfPats
244236 placement =
245237 case endOfPats of
246238 Nothing -> blockPlacement placer grhssGRHSs
247239 Just spn ->
248− if isOneLineSpan
249− (mkSrcSpan spn (srcSpanStart grhssSpan))
240+ if onTheSameLine spn grhssSpan
250241 then blockPlacement placer grhssGRHSs
251242 else Normal
252243 p_body = do
@@ -262,10 +253,18 @@ p_match' placer render style isInfix strictness m_pats m_grhss = do
262253 txt "where"
263254 unless whereIsEmpty breakpoint
264255 inci $ located grhssLocalBinds p_hsLocalBinds
265− inci' $ do
256+ inciIf indentBody $ do
257+ unless (length grhssGRHSs > 1) $
258+ case style of
259+ Function _ | hasGuards -> return ()
260+ Function _ -> space >> inci equals
261+ PatternBind -> space >> inci equals
262+ s | isCase s && hasGuards -> return ()
263+ _ -> space >> txt "->"
266264 switchLayout [patGrhssSpan] $
267265 placeHanging placement p_body
268266 inci p_where
267+p_match' _ _ _ _ _ _ (XGRHSs x) = noExtCon x
269268
270269p_grhs :: GroupStyle -> GRHS GhcPs (LHsExpr GhcPs) -> R ()
271270p_grhs = p_grhs' exprPlacement p_hsExpr
@@ -279,79 +278,109 @@ p_grhs' ::
279278 GroupStyle ->
280279 GRHS GhcPs (Located body) ->
281280 R ()
282−p_grhs' placer render style (GRHS NoExt guards body) =
281+p_grhs' placer render style (GRHS NoExtField guards body) =
283282 case guards of
284283 [] -> p_body
285284 xs -> do
286285 txt "|"
287286 space
288− sitcc (sep (comma >> breakpoint) (sitcc . located' p_stmt) xs)
287+ sitcc (sep commaDel (sitcc . located' p_stmt) xs)
289288 space
290− txt $ case style of
291− EqualSign -> "="
292− RightArrow -> "->"
289+ inci $ case style of
290+ EqualSign -> equals
291+ RightArrow -> txt "->"
293292 placeHanging placement p_body
294293 where
295294 placement =
296295 case endOfGuards of
297296 Nothing -> placer (unLoc body)
298297 Just spn ->
299− if isOneLineSpan (mkSrcSpan spn (srcSpanStart (getLoc body)))
298+ if onTheSameLine spn (getLoc body)
300299 then placer (unLoc body)
301300 else Normal
302301 endOfGuards =
303302 case NE.nonEmpty guards of
304303 Nothing -> Nothing
305− Just gs -> (Just . srcSpanEnd . getLoc . NE.last) gs
304+ Just gs -> (Just . getLoc . NE.last) gs
306305 p_body = located body render
307−p_grhs' _ _ _ (XGRHS NoExt) = notImplemented "XGRHS"
306+p_grhs' _ _ _ (XGRHS x) = noExtCon x
308307
309308p_hsCmd :: HsCmd GhcPs -> R ()
310309p_hsCmd = \case
311− HsCmdArrApp NoExt body input arrType _ -> do
310+ HsCmdArrApp NoExtField body input arrType _ -> do
312311 located body p_hsExpr
313− space
314− case arrType of
315− HsFirstOrderApp -> txt "-<"
316− HsHigherOrderApp -> txt "-<<"
317− placeHanging (exprPlacement (unLoc input)) $
318− located input p_hsExpr
319− HsCmdArrForm NoExt form Prefix _ cmds -> banana $ sitcc $ do
312+ breakpoint
313+ inci $ do
314+ case arrType of
315+ HsFirstOrderApp -> txt "-<"
316+ HsHigherOrderApp -> txt "-<<"
317+ placeHanging (exprPlacement (unLoc input)) $
318+ located input p_hsExpr
319+ HsCmdArrForm NoExtField form Prefix _ cmds -> banana $ do
320320 located form p_hsExpr
321321 unless (null cmds) $ do
322322 breakpoint
323323 inci (sequence_ (intersperse breakpoint (located' p_hsCmdTop <$> cmds)))
324− HsCmdArrForm NoExt form Infix _ [left, right] -> do
324+ HsCmdArrForm NoExtField form Infix _ [left, right] -> do
325325 located left p_hsCmdTop
326326 space
327327 located form p_hsExpr
328328 placeHanging (cmdTopPlacement (unLoc right)) $
329329 located right p_hsCmdTop
330− HsCmdArrForm NoExt _ Infix _ _ -> notImplemented "HsCmdArrForm"
330+ HsCmdArrForm NoExtField _ Infix _ _ -> notImplemented "HsCmdArrForm"
331331 HsCmdApp {} ->
332332 -- XXX Does this ever occur in the syntax tree? It does not seem like it
333333 -- does. Open an issue and ping @yumiova if this ever occurs in output.
334334 notImplemented "HsCmdApp"
335− HsCmdLam NoExt mgroup -> p_matchGroup' cmdPlacement p_hsCmd Lambda mgroup
336− HsCmdPar NoExt c -> parens N (located c p_hsCmd)
337− HsCmdCase NoExt e mgroup ->
335+ HsCmdLam NoExtField mgroup -> p_matchGroup' cmdPlacement p_hsCmd Lambda mgroup
336+ HsCmdPar NoExtField c -> parens N (located c p_hsCmd)
337+ HsCmdCase NoExtField e mgroup ->
338338 p_case cmdPlacement p_hsCmd e mgroup
339− HsCmdIf NoExt _ if' then' else' ->
339+ HsCmdIf NoExtField _ if' then' else' ->
340340 p_if cmdPlacement p_hsCmd if' then' else'
341− HsCmdLet NoExt localBinds c ->
341+ HsCmdLet NoExtField localBinds c ->
342342 p_let p_hsCmd localBinds c
343− HsCmdDo NoExt es -> do
343+ HsCmdDo NoExtField es -> do
344344 txt "do"
345345 newline
346346 inci . located es $
347− sitcc . sep newline (located' (sitcc . p_stmt' cmdPlacement p_hsCmd))
347+ sitcc . sep newline (sitcc . withSpacing (p_stmt' cmdPlacement p_hsCmd))
348348 HsCmdWrap {} -> notImplemented "HsCmdWrap"
349− XCmd {} -> notImplemented "XCmd"
349+ XCmd x -> noExtCon x
350350
351351p_hsCmdTop :: HsCmdTop GhcPs -> R ()
352352p_hsCmdTop = \case
353− HsCmdTop NoExt cmd -> located cmd p_hsCmd
354− XCmdTop {} -> notImplemented "XHsCmdTop"
353+ HsCmdTop NoExtField cmd -> located cmd p_hsCmd
354+ XCmdTop x -> noExtCon x
355+
356+-- | Render an expression preserving blank lines between such consecutive
357+-- expressions found in the original source code.
358+withSpacing ::
359+ -- | Rendering function
360+ (a -> R ()) ->
361+ -- | Entity to render
362+ Located a ->
363+ R ()
364+withSpacing f l = located l $ \x -> do
365+ case getLoc l of
366+ UnhelpfulSpan _ -> f x
367+ RealSrcSpan currentSpn -> do
368+ getSpanMark >>= \case
369+ -- Spacing before comments will be handled by the code
370+ -- that prints comments, so we just have to deal with
371+ -- blank lines between statements here.
372+ Just (StatementSpan lastSpn) ->
373+ if srcSpanStartLine currentSpn > srcSpanEndLine lastSpn + 1
374+ then newline
375+ else return ()
376+ _ -> return ()
377+ f x
378+ -- In some cases the (f x) expression may insert a new mark. We want
379+ -- to be careful not to override comment marks.
380+ getSpanMark >>= \case
381+ Just (HaddockSpan _ _) -> return ()
382+ Just (CommentSpan _) -> return ()
383+ _ -> setSpanMark (StatementSpan currentSpn)
355384
356385p_stmt :: Stmt GhcPs (LHsExpr GhcPs) -> R ()
357386p_stmt = p_stmt' exprPlacement p_hsExpr
@@ -366,16 +395,13 @@ p_stmt' ::
366395 Stmt GhcPs (Located body) ->
367396 R ()
368397p_stmt' placer render = \case
369− LastStmt NoExt body _ _ -> located body render
370− BindStmt NoExt p f _ _ -> do
371− p_pat p
398+ LastStmt NoExtField body _ _ -> located body render
399+ BindStmt NoExtField p f _ _ -> do
400+ located p p_pat
372401 space
373402 txt "<-"
374− -- https://gitlab.haskell.org/ghc/ghc/issues/17330
375− let loc = case p of
376− XPat pat -> getLoc pat
377− _ -> error "p_stmt': BindStmt: Pat does not contain a location"
378− let placement =
403+ let loc = getLoc p
404+ placement =
379405 case f of
380406 L l' x ->
381407 if isOneLineSpan
@@ -385,20 +411,20 @@ p_stmt' placer render = \case
385411 switchLayout [loc, getLoc f] $
386412 placeHanging placement (located f render)
387413 ApplicativeStmt {} -> notImplemented "ApplicativeStmt" -- generated by renamer
388− BodyStmt NoExt body _ _ -> located body render
389− LetStmt NoExt binds -> do
414+ BodyStmt NoExtField body _ _ -> located body render
415+ LetStmt NoExtField binds -> do
390416 txt "let"
391417 space
392418 sitcc $ located binds p_hsLocalBinds
393419 ParStmt {} ->
394− -- NOTE 'ParStmt' should always be eliminated in 'gatherStmt' already,
395− -- such that it never occurs in 'p_stmt''. Consequently, handling it
396− -- here would be redundant.
420+ -- 'ParStmt' should always be eliminated in 'gatherStmt' already, such
421+ -- that it never occurs in 'p_stmt''. Consequently, handling it here
422+ -- would be redundant.
397423 notImplemented "ParStmt"
398− TransStmt {..} -> do
399− -- NOTE 'TransStmt' only needs to account for render printing itself,
400− -- since pretty printing of relevant statements (e.g., in 'trS_stmts')
401− -- is handled through 'gatherStmt'.
424+ TransStmt {..} ->
425+ -- 'TransStmt' only needs to account for render printing itself, since
426+ -- pretty printing of relevant statements (e.g., in 'trS_stmts') is
427+ -- handled through 'gatherStmt'.
402428 case (trS_form, trS_by) of
403429 (ThenForm, Nothing) -> do
404430 txt "then"
@@ -427,11 +453,11 @@ p_stmt' placer render = \case
427453 RecStmt {..} -> do
428454 txt "rec"
429455 space
430− sitcc $ sepSemi (located' (p_stmt' placer render)) recS_stmts
431− XStmtLR {} -> notImplemented "XStmtLR"
456+ sitcc $ sepSemi (withSpacing (p_stmt' placer render)) recS_stmts
457+ XStmtLR c -> noExtCon c
432458
433459gatherStmt :: ExprLStmt GhcPs -> [[ExprLStmt GhcPs]]
434−gatherStmt (L _ (ParStmt NoExt block _ _)) =
460+gatherStmt (L _ (ParStmt NoExtField block _ _)) =
435461 foldr ((<>) . gatherStmtBlock) [] block
436462gatherStmt (L s stmt@TransStmt {..}) =
437463 foldr liftAppend [] ((gatherStmt <$> trS_stmts) <> pure [[L s stmt]])
@@ -440,96 +466,93 @@ gatherStmt stmt = [[stmt]]
440466gatherStmtBlock :: ParStmtBlock GhcPs GhcPs -> [[ExprLStmt GhcPs]]
441467gatherStmtBlock (ParStmtBlock _ stmts _ _) =
442468 foldr (liftAppend . gatherStmt) [] stmts
443−gatherStmtBlock XParStmtBlock {} = notImplemented "XParStmtBlock"
469+gatherStmtBlock (XParStmtBlock x) = noExtCon x
444470
445471p_hsLocalBinds :: HsLocalBindsLR GhcPs GhcPs -> R ()
446472p_hsLocalBinds = \case
447− HsValBinds NoExt (ValBinds NoExt bag lsigs) -> do
448− let ssStart =
449− either
450− (srcSpanStart . getLoc)
451− (srcSpanStart . getLoc)
452− items =
453− (Left <$> bagToList bag) ++ (Right <$> lsigs)
454− p_item (Left x) = located x p_valDecl
455− p_item (Right x) = located x p_sigDecl
456− -- Assigns 'False' to the last element, 'True' to the rest.
457− markInit :: [a] -> [(Bool, a)]
458− markInit [] = []
459− markInit (x : []) = [(False, x)]
460− markInit (x : xs) = (True, x) : markInit xs
461− -- NOTE When in a single-line layout, there is a chance that the inner
473+ HsValBinds NoExtField (ValBinds NoExtField bag lsigs) -> do
474+ -- When in a single-line layout, there is a chance that the inner
462475 -- elements will also contain semicolons and they will confuse the
463476 -- parser. so we request braces around every element except the last.
464477 br <- layoutToBraces <$> getLayout
465− sitcc $
466− sepSemi
467− (\(m, i) -> (if m then br else id) $ p_item i)
468− (markInit $ sortOn ssStart items)
469− HsValBinds NoExt _ -> notImplemented "HsValBinds"
470− HsIPBinds NoExt (IPBinds NoExt xs) ->
478+ let items =
479+ let injectLeft (L l x) = L l (Left x)
480+ injectRight (L l x) = L l (Right x)
481+ in (injectLeft <$> bagToList bag) ++ (injectRight <$> lsigs)
482+ positionToBracing = \case
483+ SinglePos -> id
484+ FirstPos -> br
485+ MiddlePos -> br
486+ LastPos -> id
487+ p_item' (p, item) =
488+ positionToBracing p $
489+ withSpacing (either p_valDecl p_sigDecl) item
490+ binds = sortOn (srcSpanStart . getLoc) items
491+ sitcc $ sepSemi p_item' (attachRelativePos binds)
492+ HsValBinds NoExtField _ -> notImplemented "HsValBinds"
493+ HsIPBinds NoExtField (IPBinds NoExtField xs) ->
471494 -- Second argument of IPBind is always Left before type-checking.
472− let p_ipBind (IPBind NoExt (Left name) expr) = do
495+ let p_ipBind (IPBind NoExtField (Left name) expr) = do
473496 atom name
474497 space
475− txt "="
498+ equals
476499 breakpoint
477500 useBraces $ inci $ located expr p_hsExpr
478− p_ipBind _ = notImplemented "XHsIPBinds"
501+ p_ipBind (IPBind NoExtField (Right _) _) =
502+ -- Should only occur after the type checker
503+ notImplemented "IPBind _ (Right _) _"
504+ p_ipBind (XIPBind x) = noExtCon x
479505 in sepSemi (located' p_ipBind) xs
480− HsIPBinds NoExt _ -> notImplemented "HsIpBinds"
481− EmptyLocalBinds NoExt -> return ()
482− XHsLocalBindsLR _ -> notImplemented "XHsLocalBindsLR"
506+ HsIPBinds NoExtField _ -> notImplemented "HsIpBinds"
507+ EmptyLocalBinds NoExtField -> return ()
508+ XHsLocalBindsLR x -> noExtCon x
483509
484510p_hsRecField ::
485511 HsRecField' RdrName (LHsExpr GhcPs) ->
486512 R ()
487−p_hsRecField = \HsRecField {..} -> do
513+p_hsRecField HsRecField {..} = do
488514 p_rdrName hsRecFieldLbl
489515 unless hsRecPun $ do
490516 space
491− txt "="
492− let placement = exprPlacement (unLoc hsRecFieldArg)
493− placeHanging placement $ located hsRecFieldArg p_hsExpr
494−
495−p_hsTupArg :: HsTupArg GhcPs -> R ()
496−p_hsTupArg = \case
497− Present NoExt x -> located x p_hsExpr
498− Missing NoExt -> pure ()
499− XTupArg {} -> notImplemented "XTupArg"
517+ equals
518+ let placement =
519+ if onTheSameLine (getLoc hsRecFieldLbl) (getLoc hsRecFieldArg)
520+ then exprPlacement (unLoc hsRecFieldArg)
521+ else Normal
522+ placeHanging placement (located hsRecFieldArg p_hsExpr)
500523
501524p_hsExpr :: HsExpr GhcPs -> R ()
502525p_hsExpr = p_hsExpr' N
503526
504527p_hsExpr' :: BracketStyle -> HsExpr GhcPs -> R ()
505528p_hsExpr' s = \case
506− HsVar NoExt name -> p_rdrName name
507− HsUnboundVar NoExt _ -> notImplemented "HsUnboundVar"
508− HsConLikeOut NoExt _ -> notImplemented "HsConLikeOut"
509− HsRecFld NoExt x ->
529+ HsVar NoExtField name -> p_rdrName name
530+ HsUnboundVar NoExtField v -> atom (unboundVarOcc v)
531+ HsConLikeOut NoExtField _ -> notImplemented "HsConLikeOut"
532+ HsRecFld NoExtField x ->
510533 case x of
511− Unambiguous NoExt name -> p_rdrName name
512− Ambiguous NoExt name -> p_rdrName name
513− XAmbiguousFieldOcc NoExt -> notImplemented "XAmbiguousFieldOcc"
514− HsOverLabel NoExt _ v -> do
534+ Unambiguous NoExtField name -> p_rdrName name
535+ Ambiguous NoExtField name -> p_rdrName name
536+ XAmbiguousFieldOcc xx -> noExtCon xx
537+ HsOverLabel NoExtField _ v -> do
515538 txt "#"
516539 atom v
517− HsIPVar NoExt (HsIPName name) -> do
540+ HsIPVar NoExtField (HsIPName name) -> do
518541 txt "?"
519542 atom name
520− HsOverLit NoExt v -> atom (ol_val v)
521− HsLit NoExt lit ->
543+ HsOverLit NoExtField v -> atom (ol_val v)
544+ HsLit NoExtField lit ->
522545 case lit of
523546 HsString (SourceText stxt) _ -> p_stringLit stxt
524547 HsStringPrim (SourceText stxt) _ -> p_stringLit stxt
525548 r -> atom r
526− HsLam NoExt mgroup ->
549+ HsLam NoExtField mgroup ->
527550 p_matchGroup Lambda mgroup
528− HsLamCase NoExt mgroup -> do
551+ HsLamCase NoExtField mgroup -> do
529552 txt "\\case"
530553 breakpoint
531554 inci (p_matchGroup LambdaCase mgroup)
532− HsApp NoExt f x -> do
555+ HsApp NoExtField f x -> do
533556 let -- In order to format function applications with multiple parameters
534557 -- nicer, traverse the AST to gather the function and all the
535558 -- parameters together.
@@ -541,8 +564,10 @@ p_hsExpr' s = \case
541564 -- We need to handle the last argument specially if it is a
542565 -- hanging construct, so separate it from the rest.
543566 (initp, lastp) = (NE.init args, NE.last args)
544− initSpan = combineSrcSpans' $ getLoc f :| map getLoc initp
545− -- Hang the last argument only if the initial arguments spans one
567+ initSpan =
568+ combineSrcSpans' $
569+ getLoc f :| [(srcLocSpan . srcSpanStart . getLoc) lastp]
570+ -- Hang the last argument only if the initial arguments span one
546571 -- line.
547572 placement =
548573 if isOneLineSpan initSpan
@@ -554,11 +579,26 @@ p_hsExpr' s = \case
554579 -- one.
555580 case placement of
556581 Normal -> do
557− useBraces $ do
582+ let -- Usually we want to bump indentation for arguments for the
583+ -- sake of readability. However, when the function itself is a
584+ -- do-block or case expression it is not a good idea. It seems
585+ -- to be safe to always bump indentation when the function
586+ -- expression is parenthesised.
587+ doIndent =
588+ case func of
589+ L _ (HsPar NoExtField _) -> True
590+ L _ (HsAppType NoExtField _ _) -> True
591+ L _ (HsMultiIf NoExtField _) -> True
592+ L spn _ -> isOneLineSpan spn
593+ ub <-
594+ getLayout <&> \case
595+ SingleLine -> useBraces
596+ MultiLine -> id
597+ ub $ do
558598 located func (p_hsExpr' s)
559599 breakpoint
560− inci $ sep breakpoint (located' p_hsExpr) initp
561− inci $ do
600+ inciIf doIndent $ sep breakpoint (located' p_hsExpr) initp
601+ inciIf doIndent $ do
562602 unless (null initp) breakpoint
563603 located lastp p_hsExpr
564604 Hanging -> do
@@ -568,75 +608,87 @@ p_hsExpr' s = \case
568608 sep breakpoint (located' p_hsExpr) initp
569609 placeHanging placement $
570610 located lastp p_hsExpr
571− HsAppType NoExt e a -> do
611+ HsAppType NoExtField e a -> do
572612 located e p_hsExpr
573613 breakpoint
574614 inci $ do
575615 txt "@"
616+ -- Insert a space when the type is represented as a TH splice to avoid
617+ -- gluing @ and $ together.
618+ case unLoc (hswc_body a) of
619+ HsSpliceTy {} -> space
620+ _ -> return ()
576621 located (hswc_body a) p_hsType
577− OpApp NoExt x op y -> do
622+ OpApp NoExtField x op y -> do
578623 let opTree = OpBranch (exprOpTree x) op (exprOpTree y)
579− p_exprOpTree True s (reassociateOpTree getOpName opTree)
580− NegApp NoExt e _ -> do
624+ p_exprOpTree s (reassociateOpTree getOpName opTree)
625+ NegApp NoExtField e _ -> do
581626 txt "-"
582627 space
583628 located e p_hsExpr
584− HsPar NoExt e ->
629+ HsPar NoExtField e ->
585630 parens s (located e (dontUseBraces . p_hsExpr))
586− SectionL NoExt x op -> do
631+ SectionL NoExtField x op -> do
587632 located x p_hsExpr
588633 breakpoint
589634 inci (located op p_hsExpr)
590− SectionR NoExt op x -> do
635+ SectionR NoExtField op x -> do
591636 located op p_hsExpr
592− breakpoint
637+ useRecordDot' <- useRecordDot
638+ let isRecordDot' = isRecordDot (unLoc op) (getLoc x)
639+ unless (useRecordDot' && isRecordDot') breakpoint
593640 inci (located x p_hsExpr)
594− ExplicitTuple NoExt args boxity -> do
641+ ExplicitTuple NoExtField args boxity ->
595642 let isSection = any (isMissing . unLoc) args
596643 isMissing = \case
597− Missing NoExt -> True
644+ Missing NoExtField -> True
598645 _ -> False
599− let parens' =
646+ p_arg = \case
647+ Present NoExtField x -> located x p_hsExpr
648+ Missing NoExtField -> pure ()
649+ XTupArg x -> noExtCon x
650+ p_larg = sitcc . located' p_arg
651+ parens' =
600652 case boxity of
601653 Boxed -> parens
602654 Unboxed -> parensHash
603− if isSection
604− then
605− switchLayout [] . parens' s $
606− sep comma (located' p_hsTupArg) args
607− else
608− switchLayout (getLoc <$> args) . parens' s . sitcc $
609− sep (comma >> breakpoint) (sitcc . located' p_hsTupArg) args
610− ExplicitSum NoExt tag arity e ->
655+ in if isSection
656+ then
657+ switchLayout [] . parens' s $
658+ sep comma p_larg args
659+ else
660+ switchLayout (getLoc <$> args) . parens' s $
661+ sep commaDel p_larg args
662+ ExplicitSum NoExtField tag arity e ->
611663 p_unboxedSum N tag arity (located e p_hsExpr)
612− HsCase NoExt e mgroup ->
664+ HsCase NoExtField e mgroup ->
613665 p_case exprPlacement p_hsExpr e mgroup
614− HsIf NoExt _ if' then' else' ->
666+ HsIf NoExtField _ if' then' else' ->
615667 p_if exprPlacement p_hsExpr if' then' else'
616− HsMultiIf NoExt guards -> do
668+ HsMultiIf NoExtField guards -> do
617669 txt "if"
618670 breakpoint
619− inci . sitcc $ sep newline (located' (p_grhs RightArrow)) guards
620− HsLet NoExt localBinds e ->
671+ inci . inci $ sep newline (located' (p_grhs RightArrow)) guards
672+ HsLet NoExtField localBinds e ->
621673 p_let p_hsExpr localBinds e
622− HsDo NoExt ctx es -> do
674+ HsDo NoExtField ctx es -> do
623675 let doBody header = do
624676 txt header
625677 breakpoint
626678 ub <- layoutToBraces <$> getLayout
627679 inci $
628680 sepSemi
629− (located' (ub . p_stmt' exprPlacement (p_hsExpr' S)))
681+ (ub . withSpacing (p_stmt' exprPlacement (p_hsExpr' S)))
630682 (unLoc es)
631− compBody = brackets N $ located es $ \xs -> do
683+ compBody = brackets N . located es $ \xs -> do
632684 let p_parBody =
633685 sep
634− (breakpoint >> txt "| ")
686+ (breakpoint >> txt "|" >> space)
635687 p_seqBody
636688 p_seqBody =
637689 sitcc
638690 . sep
639− (comma >> breakpoint)
691+ commaDel
640692 (located' (sitcc . p_stmt))
641693 stmts = init xs
642694 yield = last xs
@@ -657,90 +709,96 @@ p_hsExpr' s = \case
657709 ParStmtCtxt _ -> notImplemented "ParStmtCtxt"
658710 TransStmtCtxt _ -> notImplemented "TransStmtCtxt"
659711 ExplicitList _ _ xs ->
660− brackets s . sitcc $
661− sep (comma >> breakpoint) (sitcc . located' p_hsExpr) xs
712+ brackets s $
713+ sep commaDel (sitcc . located' p_hsExpr) xs
662714 RecordCon {..} -> do
663715 located rcon_con_name atom
664716 breakpoint
665717 let HsRecFields {..} = rcon_flds
666718 updName f =
667− f
719+ (f :: HsRecField GhcPs (LHsExpr GhcPs))
668720 { hsRecFieldLbl = case unLoc $ hsRecFieldLbl f of
669721 FieldOcc _ n -> n
670− XFieldOcc _ -> notImplemented "XFieldOcc"
722+ XFieldOcc x -> noExtCon x
671723 }
672724 fields = located' (p_hsRecField . updName) <$> rec_flds
673725 dotdot =
674726 case rec_dotdot of
675727 Just {} -> [txt ".."]
676728 Nothing -> []
677− inci . braces N . sitcc $
678− sep (comma >> breakpoint) sitcc (fields <> dotdot)
729+ inci . braces N $
730+ sep commaDel sitcc (fields <> dotdot)
679731 RecordUpd {..} -> do
680732 located rupd_expr p_hsExpr
681− breakpoint
733+ useRecordDot' <- useRecordDot
734+ let mrs sp = case getLoc sp of
735+ RealSrcSpan r -> Just r
736+ _ -> Nothing
737+ let isPluginForm =
738+ ((1 +) . srcSpanEndCol <$> mrs rupd_expr)
739+ == (srcSpanStartCol <$> mrs (head rupd_flds))
740+ unless (useRecordDot' && isPluginForm) breakpoint
682741 let updName f =
683− f
742+ (f :: HsRecUpdField GhcPs)
684743 { hsRecFieldLbl = case unLoc $ hsRecFieldLbl f of
685744 Ambiguous _ n -> n
686745 Unambiguous _ n -> n
687− XAmbiguousFieldOcc _ -> notImplemented "XAmbiguousFieldOcc"
746+ XAmbiguousFieldOcc x -> noExtCon x
688747 }
689− inci . braces N . sitcc $
748+ inci . braces N $
690749 sep
691− (comma >> breakpoint)
750+ commaDel
692751 (sitcc . located' (p_hsRecField . updName))
693752 rupd_flds
694− ExprWithTySig NoExt x affix -> sitcc $ do
753+ ExprWithTySig NoExtField x HsWC {hswc_body = HsIB {..}} -> sitcc $ do
695754 located x p_hsExpr
696755 space
697756 txt "::"
698757 breakpoint
699− inci $ do
700− let HsWC {..} = affix
701− HsIB {..} = hswc_body
702− located hsib_body p_hsType
703− ArithSeq NoExt _ x -> do
758+ inci $ located hsib_body p_hsType
759+ ExprWithTySig NoExtField _ HsWC {hswc_body = XHsImplicitBndrs x} -> noExtCon x
760+ ExprWithTySig NoExtField _ (XHsWildCardBndrs x) -> noExtCon x
761+ ArithSeq NoExtField _ x ->
704762 case x of
705− From from -> brackets s . sitcc $ do
763+ From from -> brackets s $ do
706764 located from p_hsExpr
707765 breakpoint
708766 txt ".."
709− FromThen from next -> brackets s . sitcc $ do
710− sitcc $ sep (comma >> breakpoint) (located' p_hsExpr) [from, next]
767+ FromThen from next -> brackets s $ do
768+ sep commaDel (located' p_hsExpr) [from, next]
711769 breakpoint
712770 txt ".."
713− FromTo from to -> brackets s . sitcc $ do
771+ FromTo from to -> brackets s $ do
714772 located from p_hsExpr
715773 breakpoint
716774 txt ".."
717775 space
718776 located to p_hsExpr
719− FromThenTo from next to -> brackets s . sitcc $ do
720− sitcc $ sep (comma >> breakpoint) (located' p_hsExpr) [from, next]
777+ FromThenTo from next to -> brackets s $ do
778+ sep commaDel (located' p_hsExpr) [from, next]
721779 breakpoint
722780 txt ".."
723781 space
724782 located to p_hsExpr
725− HsSCC NoExt _ name x -> do
783+ HsSCC NoExtField _ name x -> do
726784 txt "{-# SCC "
727785 atom name
728786 txt " #-}"
729787 breakpoint
730788 located x p_hsExpr
731− HsCoreAnn NoExt _ value x -> do
789+ HsCoreAnn NoExtField _ value x -> do
732790 txt "{-# CORE "
733791 atom value
734792 txt " #-}"
735793 breakpoint
736794 located x p_hsExpr
737− HsBracket NoExt x -> p_hsBracket x
795+ HsBracket NoExtField x -> p_hsBracket x
738796 HsRnBracketOut {} -> notImplemented "HsRnBracketOut"
739797 HsTcBracketOut {} -> notImplemented "HsTcBracketOut"
740− HsSpliceE NoExt splice -> p_hsSplice splice
741− HsProc NoExt p e -> do
798+ HsSpliceE NoExtField splice -> p_hsSplice splice
799+ HsProc NoExtField p e -> do
742800 txt "proc"
743− locatedPat p $ \x -> do
801+ located p $ \x -> do
744802 breakpoint
745803 inci (p_pat x)
746804 breakpoint
@@ -751,31 +809,11 @@ p_hsExpr' s = \case
751809 txt "static"
752810 breakpoint
753811 inci (located e p_hsExpr)
754− HsArrApp NoExt body input arrType cond ->
755− p_hsCmd (HsCmdArrApp NoExt body input arrType cond)
756− HsArrForm NoExt form mfixity cmds ->
757− p_hsCmd (HsCmdArrForm NoExt form Prefix mfixity cmds)
758812 HsTick {} -> notImplemented "HsTick"
759813 HsBinTick {} -> notImplemented "HsBinTick"
760814 HsTickPragma {} -> notImplemented "HsTickPragma"
761− -- These four constructs should never appear in correct programs.
762− -- See: https://github.com/tweag/ormolu/issues/343
763− EWildPat NoExt -> txt "_"
764− EAsPat NoExt n p -> do
765− p_rdrName n
766− txt "@"
767− located p p_hsExpr
768− EViewPat NoExt p e -> do
769− located p p_hsExpr
770− space
771− txt "->"
772− breakpoint
773− inci (located e p_hsExpr)
774− ELazyPat NoExt p -> do
775− txt "~"
776− located p p_hsExpr
777815 HsWrap {} -> notImplemented "HsWrap"
778− XExpr {} -> notImplemented "XExpr"
816+ XExpr x -> noExtCon x
779817
780818p_patSynBind :: PatSynBind GhcPs GhcPs -> R ()
781819p_patSynBind PSB {..} = do
@@ -785,15 +823,15 @@ p_patSynBind PSB {..} = do
785823 Unidirectional -> do
786824 txt "<-"
787825 breakpoint
788− p_pat psb_def
826+ located psb_def p_pat
789827 ImplicitBidirectional -> do
790− txt "="
828+ equals
791829 breakpoint
792− p_pat psb_def
830+ located psb_def p_pat
793831 ExplicitBidirectional mgroup -> do
794832 txt "<-"
795833 breakpoint
796− p_pat psb_def
834+ located psb_def p_pat
797835 newline
798836 txt "where"
799837 newline
@@ -814,8 +852,8 @@ p_patSynBind PSB {..} = do
814852 inci $ do
815853 switchLayout (getLoc . recordPatSynPatVar <$> xs) $ do
816854 unless (null xs) breakpoint
817− braces N . sitcc $
818− sep (comma >> breakpoint) (p_rdrName . recordPatSynPatVar) xs
855+ braces N $
856+ sep commaDel (p_rdrName . recordPatSynPatVar) xs
819857 rhs
820858 InfixCon l r -> do
821859 switchLayout [getLoc l, getLoc r] $ do
@@ -827,7 +865,7 @@ p_patSynBind PSB {..} = do
827865 space
828866 p_rdrName r
829867 inci rhs
830−p_patSynBind (XPatSynBind NoExt) = notImplemented "XPatSynBind"
868+p_patSynBind (XPatSynBind x) = noExtCon x
831869
832870p_case ::
833871 Data body =>
@@ -838,7 +876,7 @@ p_case ::
838876 -- | Expression
839877 LHsExpr GhcPs ->
840878 -- | Match group
841− (MatchGroup GhcPs (Located body)) ->
879+ MatchGroup GhcPs (Located body) ->
842880 R ()
843881p_case placer render e mgroup = do
844882 txt "case"
@@ -869,10 +907,12 @@ p_if placer render if' then' else' = do
869907 breakpoint
870908 inci $ do
871909 txt "then"
910+ space
872911 located then' $ \x ->
873912 placeHanging (placer x) (render x)
874913 breakpoint
875914 txt "else"
915+ space
876916 located else' $ \x ->
877917 placeHanging (placer x) (render x)
878918
@@ -894,77 +934,77 @@ p_let render localBinds e = sitcc $ do
894934
895935p_pat :: Pat GhcPs -> R ()
896936p_pat = \case
897− -- Note: starting from GHC 8.8, 'LPat' == 'Pat'. Located 'Pat's are always
898− -- constructed with the 'XPat' constructor, containing a @Located Pat@.
899− XPat pat -> located pat p_pat
900− WildPat NoExt -> txt "_"
901− VarPat NoExt name -> p_rdrName name
902− LazyPat NoExt pat -> do
937+ WildPat NoExtField -> txt "_"
938+ VarPat NoExtField name -> p_rdrName name
939+ LazyPat NoExtField pat -> do
903940 txt "~"
904− p_pat pat
905− AsPat NoExt name pat -> do
941+ located pat p_pat
942+ AsPat NoExtField name pat -> do
906943 p_rdrName name
907944 txt "@"
908− p_pat pat
909− ParPat NoExt pat ->
910− locatedPat pat (parens S . p_pat)
911− BangPat NoExt pat -> do
945+ located pat p_pat
946+ ParPat NoExtField pat ->
947+ located pat (parens S . p_pat)
948+ BangPat NoExtField pat -> do
912949 txt "!"
913− p_pat pat
914− ListPat NoExt pats -> do
915− brackets S . sitcc $ sep (comma >> breakpoint) p_pat pats
916− TuplePat NoExt pats boxing -> do
917− let f =
950+ located pat p_pat
951+ ListPat NoExtField pats ->
952+ brackets S $ sep commaDel (located' p_pat) pats
953+ TuplePat NoExtField pats boxing -> do
954+ let parens' =
918955 case boxing of
919956 Boxed -> parens S
920957 Unboxed -> parensHash S
921− f . sitcc $ sep (comma >> breakpoint) (sitcc . p_pat) pats
922− SumPat NoExt pat tag arity ->
923− p_unboxedSum S tag arity (p_pat pat)
958+ parens' $ sep commaDel (sitcc . located' p_pat) pats
959+ SumPat NoExtField pat tag arity ->
960+ p_unboxedSum S tag arity (located pat p_pat)
924961 ConPatIn pat details ->
925962 case details of
926963 PrefixCon xs -> sitcc $ do
927964 p_rdrName pat
928965 unless (null xs) $ do
929966 breakpoint
930− inci . sitcc $ sep breakpoint (sitcc . p_pat) xs
967+ inci . sitcc $ sep breakpoint (sitcc . located' p_pat) xs
931968 RecCon (HsRecFields fields dotdot) -> do
932969 p_rdrName pat
933970 breakpoint
934971 let f = \case
935972 Nothing -> txt ".."
936973 Just x -> located x p_pat_hsRecField
937− inci . braces N . sitcc . sep (comma >> breakpoint) f $
974+ inci . braces N . sep commaDel f $
938975 case dotdot of
939976 Nothing -> Just <$> fields
940− Just n -> (Just <$> take n fields) ++ [Nothing]
941− InfixCon x y -> do
942− p_pat x
943− space
944− p_rdrName pat
945− breakpoint
946− inci (p_pat y)
977+ Just (L _ n) -> (Just <$> take n fields) ++ [Nothing]
978+ InfixCon l r -> do
979+ switchLayout [getLoc l, getLoc r] $ do
980+ located l p_pat
981+ breakpoint
982+ inci $ do
983+ p_rdrName pat
984+ space
985+ located r p_pat
947986 ConPatOut {} -> notImplemented "ConPatOut" -- presumably created by renamer?
948− ViewPat NoExt expr pat -> sitcc $ do
987+ ViewPat NoExtField expr pat -> sitcc $ do
949988 located expr p_hsExpr
950989 space
951990 txt "->"
952991 breakpoint
953− inci (p_pat pat)
954− SplicePat NoExt splice -> p_hsSplice splice
955− LitPat NoExt p -> atom p
956− NPat NoExt v _ _ -> located v (atom . ol_val)
957− NPlusKPat NoExt n k _ _ _ -> sitcc $ do
992+ inci (located pat p_pat)
993+ SplicePat NoExtField splice -> p_hsSplice splice
994+ LitPat NoExtField p -> atom p
995+ NPat NoExtField v _ _ -> located v (atom . ol_val)
996+ NPlusKPat NoExtField n k _ _ _ -> sitcc $ do
958997 p_rdrName n
959998 breakpoint
960999 inci $ do
9611000 txt "+"
9621001 space
9631002 located k (atom . ol_val)
964− SigPat NoExt pat hswc -> do
965− p_pat pat
1003+ SigPat NoExtField pat hswc -> do
1004+ located pat p_pat
9661005 p_typeAscription hswc
9671006 CoPat {} -> notImplemented "CoPat" -- apparently created at some later stage
1007+ XPat x -> noExtCon x
9681008
9691009p_pat_hsRecField :: HsRecField' (FieldOcc GhcPs) (LPat GhcPs) -> R ()
9701010p_pat_hsRecField HsRecField {..} = do
@@ -972,42 +1012,40 @@ p_pat_hsRecField HsRecField {..} = do
9721012 p_rdrName (rdrNameFieldOcc x)
9731013 unless hsRecPun $ do
9741014 space
975− txt "="
1015+ equals
9761016 breakpoint
977− inci (p_pat hsRecFieldArg)
1017+ inci (located hsRecFieldArg p_pat)
9781018
9791019p_unboxedSum :: BracketStyle -> ConTag -> Arity -> R () -> R ()
9801020p_unboxedSum s tag arity m = do
9811021 let before = tag - 1
9821022 after = arity - before - 1
9831023 args = replicate before Nothing <> [Just m] <> replicate after Nothing
984− f (x, i) = do
985− let isFirst = i == 0
986− isLast = i == arity - 1
1024+ f x =
9871025 case x :: Maybe (R ()) of
9881026 Nothing ->
989− unless (isFirst || isLast) space
1027+ space
9901028 Just m' -> do
991− unless isFirst space
1029+ space
9921030 m'
993− unless isLast space
994− parensHash s $ sep (txt "|") f (zip args [0 ..])
1031+ space
1032+ parensHash s $ sep (txt "|") f args
9951033
9961034p_hsSplice :: HsSplice GhcPs -> R ()
9971035p_hsSplice = \case
998− HsTypedSplice NoExt deco _ expr -> p_hsSpliceTH True expr deco
999− HsUntypedSplice NoExt deco _ expr -> p_hsSpliceTH False expr deco
1000− HsQuasiQuote NoExt _ quoterName srcSpan str -> do
1036+ HsTypedSplice NoExtField deco _ expr -> p_hsSpliceTH True expr deco
1037+ HsUntypedSplice NoExtField deco _ expr -> p_hsSpliceTH False expr deco
1038+ HsQuasiQuote NoExtField _ quoterName srcSpan str -> do
10011039 txt "["
10021040 p_rdrName (L srcSpan quoterName)
10031041 txt "|"
1004− -- NOTE QuasiQuoters often rely on precise custom strings. We cannot do
1005− -- any formatting here without potentially breaking someone's code.
1042+ -- QuasiQuoters often rely on precise custom strings. We cannot do any
1043+ -- formatting here without potentially breaking someone's code.
10061044 atom str
10071045 txt "|]"
10081046 HsSpliced {} -> notImplemented "HsSpliced"
10091047 HsSplicedT {} -> notImplemented "HsSplicedT"
1010− XSplice {} -> notImplemented "XSplice"
1048+ XSplice x -> noExtCon x
10111049
10121050p_hsSpliceTH ::
10131051 -- | Typed splice?
@@ -1031,17 +1069,17 @@ p_hsSpliceTH isTyped expr = \case
10311069
10321070p_hsBracket :: HsBracket GhcPs -> R ()
10331071p_hsBracket = \case
1034− ExpBr NoExt expr -> do
1072+ ExpBr NoExtField expr -> do
10351073 anns <- getEnclosingAnns
10361074 let name = case anns of
10371075 AnnOpenEQ : _ -> ""
10381076 _ -> "e"
10391077 quote name (located expr p_hsExpr)
1040− PatBr NoExt pat -> quote "p" (p_pat pat)
1041− DecBrL NoExt decls -> quote "d" (p_hsDecls Free decls)
1042− DecBrG NoExt _ -> notImplemented "DecBrG" -- result of renamer
1043− TypBr NoExt ty -> quote "t" (located ty p_hsType)
1044− VarBr NoExt isSingleQuote name -> do
1078+ PatBr NoExtField pat -> located pat (quote "p" . p_pat)
1079+ DecBrL NoExtField decls -> quote "d" (p_hsDecls Free decls)
1080+ DecBrG NoExtField _ -> notImplemented "DecBrG" -- result of renamer
1081+ TypBr NoExtField ty -> quote "t" (located ty p_hsType)
1082+ VarBr NoExtField isSingleQuote name -> do
10451083 txt (bool "''" "'" isSingleQuote)
10461084 -- HACK As you can see we use 'noLoc' here to be able to pass name into
10471085 -- 'p_rdrName' since the latter expects a "located" thing. The problem
@@ -1056,13 +1094,13 @@ p_hsBracket = \case
10561094 && not (doesNotNeedExtraParens name)
10571095 wrapper = if isOperator then parens N else id
10581096 wrapper $ p_rdrName (noLoc name)
1059− TExpBr NoExt expr -> do
1097+ TExpBr NoExtField expr -> do
10601098 txt "[||"
10611099 breakpoint'
10621100 located expr p_hsExpr
10631101 breakpoint'
10641102 txt "||]"
1065− XBracket {} -> notImplemented "XBracket"
1103+ XBracket x -> noExtCon x
10661104 where
10671105 quote :: Text -> R () -> R ()
10681106 quote name body = do
@@ -1141,9 +1179,9 @@ liftAppend (x : xs) [] = x : xs
11411179liftAppend (x : xs) (y : ys) = x <> y : liftAppend xs ys
11421180
11431181getGRHSSpan :: GRHS GhcPs (Located body) -> SrcSpan
1144−getGRHSSpan (GRHS NoExt guards body) =
1182+getGRHSSpan (GRHS NoExtField guards body) =
11451183 combineSrcSpans' $ getLoc body :| map getLoc guards
1146−getGRHSSpan (XGRHS NoExt) = notImplemented "XGRHS"
1184+getGRHSSpan (XGRHS x) = noExtCon x
11471185
11481186-- | Place a thing that may have a hanging form. This function handles how
11491187-- to separate it from preceding expressions and whether to bump indentation
@@ -1164,82 +1202,77 @@ blockPlacement ::
11641202 (body -> Placement) ->
11651203 [LGRHS GhcPs (Located body)] ->
11661204 Placement
1167−blockPlacement placer [(L _ (GRHS NoExt _ (L _ x)))] = placer x
1205+blockPlacement placer [L _ (GRHS NoExtField _ (L _ x))] = placer x
11681206blockPlacement _ _ = Normal
11691207
11701208-- | Check if given command has a hanging form.
11711209cmdPlacement :: HsCmd GhcPs -> Placement
11721210cmdPlacement = \case
1173− HsCmdLam NoExt _ -> Hanging
1174− HsCmdCase NoExt _ _ -> Hanging
1175− HsCmdDo NoExt _ -> Hanging
1211+ HsCmdLam NoExtField _ -> Hanging
1212+ HsCmdCase NoExtField _ _ -> Hanging
1213+ HsCmdDo NoExtField _ -> Hanging
11761214 _ -> Normal
11771215
11781216cmdTopPlacement :: HsCmdTop GhcPs -> Placement
11791217cmdTopPlacement = \case
1180− HsCmdTop NoExt (L _ x) -> cmdPlacement x
1181− XCmdTop {} -> notImplemented "XCmdTop"
1218+ HsCmdTop NoExtField (L _ x) -> cmdPlacement x
1219+ XCmdTop x -> noExtCon x
11821220
11831221-- | Check if given expression has a hanging form.
11841222exprPlacement :: HsExpr GhcPs -> Placement
11851223exprPlacement = \case
11861224 -- Only hang lambdas with single line parameter lists
1187− HsLam NoExt mg -> case mg of
1188− MG _ (L _ [L _ (Match NoExt _ (x : xs) _)]) _
1225+ HsLam NoExtField mg -> case mg of
1226+ MG _ (L _ [L _ (Match NoExtField _ (x : xs) _)]) _
11891227 | isOneLineSpan (combineSrcSpans' $ fmap getLoc (x :| xs)) ->
11901228 Hanging
11911229 _ -> Normal
1192− HsLamCase NoExt _ -> Hanging
1193− HsCase NoExt _ _ -> Hanging
1194− HsDo NoExt DoExpr _ -> Hanging
1195− HsDo NoExt MDoExpr _ -> Hanging
1196− RecordCon NoExt _ _ -> Hanging
1197− -- If the rightmost expression in an operator chain is hanging, make the
1198− -- whole block hanging; so that we can use the common @f = foo $ do@
1199− -- style.
1200− OpApp NoExt _ _ y -> exprPlacement (unLoc y)
1201− -- Same thing for function applications (usually with -XBlockArguments)
1202− HsApp NoExt _ y -> exprPlacement (unLoc y)
1203− HsProc NoExt p _ ->
1204− -- https://gitlab.haskell.org/ghc/ghc/issues/17330
1205− let loc = case p of
1206− XPat pat -> getLoc pat
1207− _ -> error "exprPlacement: HsProc: Pat does not contain a location"
1208− in -- Indentation breaks if pattern is longer than one line and left
1209− -- hanging. Consequently, only apply hanging when it is safe.
1210− if isOneLineSpan loc
1211− then Hanging
1212− else Normal
1230+ HsLamCase NoExtField _ -> Hanging
1231+ HsCase NoExtField _ _ -> Hanging
1232+ HsDo NoExtField DoExpr _ -> Hanging
1233+ HsDo NoExtField MDoExpr _ -> Hanging
1234+ OpApp NoExtField _ op y ->
1235+ case (fmap getOpNameStr . getOpName . unLoc) op of
1236+ Just "$" -> exprPlacement (unLoc y)
1237+ _ -> Normal
1238+ HsApp NoExtField _ y -> exprPlacement (unLoc y)
1239+ HsProc NoExtField p _ ->
1240+ -- Indentation breaks if pattern is longer than one line and left
1241+ -- hanging. Consequently, only apply hanging when it is safe.
1242+ if isOneLineSpan (getLoc p)
1243+ then Hanging
1244+ else Normal
12131245 _ -> Normal
12141246
12151247withGuards :: [LGRHS GhcPs (Located body)] -> Bool
12161248withGuards = any (checkOne . unLoc)
12171249 where
12181250 checkOne :: GRHS GhcPs (Located body) -> Bool
1219− checkOne (GRHS NoExt [] _) = False
1251+ checkOne (GRHS NoExtField [] _) = False
12201252 checkOne _ = True
12211253
12221254exprOpTree :: LHsExpr GhcPs -> OpTree (LHsExpr GhcPs) (LHsExpr GhcPs)
1223−exprOpTree (L _ (OpApp NoExt x op y)) = OpBranch (exprOpTree x) op (exprOpTree y)
1255+exprOpTree (L _ (OpApp NoExtField x op y)) = OpBranch (exprOpTree x) op (exprOpTree y)
12241256exprOpTree n = OpNode n
12251257
12261258getOpName :: HsExpr GhcPs -> Maybe RdrName
12271259getOpName = \case
1228− HsVar NoExt (L _ a) -> Just a
1260+ HsVar NoExtField (L _ a) -> Just a
12291261 _ -> Nothing
12301262
1263+getOpNameStr :: RdrName -> String
1264+getOpNameStr = occNameString . rdrNameOcc
1265+
12311266p_exprOpTree ::
1232− -- | Can use special handling of dollar?
1233− Bool ->
12341267 -- | Bracket style to use
12351268 BracketStyle ->
12361269 OpTree (LHsExpr GhcPs) (LHsExpr GhcPs) ->
12371270 R ()
1238−p_exprOpTree _ s (OpNode x) = located x (p_hsExpr' s)
1239−p_exprOpTree isDollarSpecial s (OpBranch x op y) = do
1240− -- NOTE If the beginning of the first argument and the second argument
1241− -- are on the same line, and the second argument has a hanging form, use
1242− -- hanging placement.
1271+p_exprOpTree s (OpNode x) = located x (p_hsExpr' s)
1272+p_exprOpTree s (OpBranch x op y) = do
1273+ -- If the beginning of the first argument and the second argument are on
1274+ -- the same line, and the second argument has a hanging form, use hanging
1275+ -- placement.
12431276 let placement =
12441277 if isOneLineSpan
12451278 (mkSrcSpan (srcSpanStart (opTreeLoc x)) (srcSpanStart (opTreeLoc y)))
@@ -1247,8 +1280,10 @@ p_exprOpTree isDollarSpecial s (OpBranch x op y) = do
12471280 OpNode (L _ n) -> exprPlacement n
12481281 _ -> Normal
12491282 else Normal
1283+ -- Distinguish holes used in infix notation.
1284+ -- eg. '1 _foo 2' and '1 `_foo` 2'
12501285 opWrapper = case unLoc op of
1251− EWildPat NoExt -> backticks
1286+ HsUnboundVar NoExtField _ -> backticks
12521287 _ -> id
12531288 layout <- getLayout
12541289 let ub = case layout of
@@ -1256,24 +1291,67 @@ p_exprOpTree isDollarSpecial s (OpBranch x op y) = do
12561291 MultiLine -> case placement of
12571292 Hanging -> useBraces
12581293 Normal -> dontUseBraces
1259− gotDollar = case getOpName (unLoc op) of
1260− Just rname -> mkVarOcc "$" == (rdrNameOcc rname)
1294+ opNameStr = (fmap getOpNameStr . getOpName . unLoc) op
1295+ gotDollar = opNameStr == Just "$"
1296+ gotColon = opNameStr == Just ":"
1297+ gotRecordDot = isRecordDot (unLoc op) (opTreeLoc y)
1298+ lhs =
1299+ switchLayout [opTreeLoc x] $
1300+ p_exprOpTree s x
1301+ p_op = located op (opWrapper . p_hsExpr)
1302+ p_y = switchLayout [opTreeLoc y] (p_exprOpTree N y)
1303+ isSection = case (opTreeLoc x, getLoc op) of
1304+ (RealSrcSpan treeSpan, RealSrcSpan opSpan) ->
1305+ srcSpanEndCol treeSpan /= srcSpanStartCol opSpan
12611306 _ -> False
1262− switchLayout [opTreeLoc x]
1263− $ ub
1264− $ p_exprOpTree (not gotDollar) s x
1265− let p_op = located op (opWrapper . p_hsExpr)
1266− p_y = switchLayout [opTreeLoc y] (p_exprOpTree True N y)
1267− if isDollarSpecial && gotDollar && placement == Normal && isOneLineSpan (opTreeLoc x)
1268− then do
1269− space
1270− p_op
1271− breakpoint
1272− inci p_y
1273− else placeHanging placement $ do
1274− p_op
1275− space
1276− p_y
1307+ isDoBlock = \case
1308+ OpNode (L _ HsDo {}) -> True
1309+ _ -> False
1310+ useRecordDot' <- useRecordDot
1311+ if
1312+ | gotColon -> do
1313+ lhs
1314+ space
1315+ p_op
1316+ case placement of
1317+ Hanging -> do
1318+ space
1319+ p_y
1320+ Normal -> do
1321+ breakpoint
1322+ inciIf (isDoBlock y) p_y
1323+ | gotDollar
1324+ && isOneLineSpan (opTreeLoc x)
1325+ && placement == Normal -> do
1326+ useBraces lhs
1327+ space
1328+ p_op
1329+ breakpoint
1330+ inci p_y
1331+ | useRecordDot' && gotRecordDot -> do
1332+ lhs
1333+ when isSection space
1334+ p_op
1335+ p_y
1336+ | otherwise -> do
1337+ ub lhs
1338+ placeHanging placement $ do
1339+ p_op
1340+ space
1341+ p_y
1342+
1343+-- | Return 'True' if given expression is a record-dot operator expression.
1344+isRecordDot ::
1345+ -- | Operator expression
1346+ HsExpr GhcPs ->
1347+ -- | Span of the expression on the right-hand side of the operator
1348+ SrcSpan ->
1349+ Bool
1350+isRecordDot op (RealSrcSpan ySpan) = case op of
1351+ HsVar NoExtField (L (RealSrcSpan opSpan) opName) ->
1352+ (getOpNameStr opName == ".") && (srcSpanEndCol opSpan == srcSpanStartCol ySpan)
1353+ _ -> False
1354+isRecordDot _ _ = False
12771355
12781356-- | Get annotations for the enclosing element.
12791357getEnclosingAnns :: R [AnnKeywordId]
src/Ormolu/Printer/Meat/Declaration/Value.hs-boot−4
@@ -11,11 +11,7 @@ import GHC
1111import Ormolu.Printer.Combinators
1212
1313p_valDecl :: HsBindLR GhcPs GhcPs -> R ()
14−
1514p_pat :: Pat GhcPs -> R ()
16−
1715p_hsExpr :: HsExpr GhcPs -> R ()
18−
1916p_hsSplice :: HsSplice GhcPs -> R ()
20−
2117p_stringLit :: String -> R ()
src/Ormolu/Printer/Meat/Declaration/Warning.hs+8−10
@@ -13,30 +13,28 @@ import Data.Text (Text)
1313import GHC
1414import Ormolu.Printer.Combinators
1515import Ormolu.Printer.Meat.Common
16−import Ormolu.Utils
1716
1817p_warnDecls :: WarnDecls GhcPs -> R ()
19−p_warnDecls (Warnings NoExt _ warnings) =
18+p_warnDecls (Warnings NoExtField _ warnings) =
2019 traverse_ (located' p_warnDecl) warnings
21−p_warnDecls XWarnDecls {} = notImplemented "XWarnDecls"
20+p_warnDecls (XWarnDecls x) = noExtCon x
2221
2322p_warnDecl :: WarnDecl GhcPs -> R ()
24−p_warnDecl (Warning NoExt functions warningTxt) =
23+p_warnDecl (Warning NoExtField functions warningTxt) =
2524 p_topLevelWarning functions warningTxt
26−p_warnDecl XWarnDecl {} = notImplemented "XWarnDecl"
25+p_warnDecl (XWarnDecl x) = noExtCon x
2726
2827p_moduleWarning :: WarningTxt -> R ()
2928p_moduleWarning wtxt = do
3029 let (pragmaText, lits) = warningText wtxt
31− switchLayout (getLoc <$> lits) $ do
32− inci $ pragma pragmaText (inci $ p_lits lits)
30+ inci $ pragma pragmaText $ inci $ p_lits lits
3331
3432p_topLevelWarning :: [Located RdrName] -> WarningTxt -> R ()
3533p_topLevelWarning fnames wtxt = do
3634 let (pragmaText, lits) = warningText wtxt
37− switchLayout (fmap getLoc fnames ++ fmap getLoc lits) $ do
35+ switchLayout (fmap getLoc fnames ++ fmap getLoc lits) $
3836 pragma pragmaText . inci $ do
39− sitcc $ sep (comma >> breakpoint) p_rdrName fnames
37+ sep commaDel p_rdrName fnames
4038 breakpoint
4139 p_lits lits
4240
@@ -48,4 +46,4 @@ warningText = \case
4846p_lits :: [Located StringLiteral] -> R ()
4947p_lits = \case
5048 [l] -> atom l
51− ls -> brackets N . sitcc $ sep (comma >> breakpoint) atom ls
49+ ls -> brackets N $ sep commaDel atom ls
src/Ormolu/Printer/Meat/ImportExport.hs+45−36
@@ -11,10 +11,9 @@ where
1111
1212import Control.Monad
1313import GHC
14−import HsImpExp (IE (..))
1514import Ormolu.Printer.Combinators
1615import Ormolu.Printer.Meat.Common
17−import Ormolu.Utils
16+import Ormolu.Utils (RelativePos (..), attachRelativePos)
1817
1918p_hsmodExports :: [LIE GhcPs] -> R ()
2019p_hsmodExports [] = do
@@ -22,19 +21,24 @@ p_hsmodExports [] = do
2221 breakpoint'
2322 txt ")"
2423p_hsmodExports xs =
25− parens N . sitcc $ do
24+ parens N $ do
2625 layout <- getLayout
27− sep breakpoint (sitcc . located' (uncurry (p_lie layout))) (attachPositions xs)
26+ sep
27+ breakpoint
28+ (\(p, l) -> sitcc (located l (p_lie layout p)))
29+ (attachRelativePos xs)
2830
29−p_hsmodImport :: ImportDecl GhcPs -> R ()
30−p_hsmodImport ImportDecl {..} = do
31+p_hsmodImport :: Bool -> ImportDecl GhcPs -> R ()
32+p_hsmodImport useQualifiedPost ImportDecl {..} = do
3133 txt "import"
3234 space
3335 when ideclSource (txt "{-# SOURCE #-}")
3436 space
3537 when ideclSafe (txt "safe")
3638 space
37− when ideclQualified (txt "qualified")
39+ when
40+ (isImportDeclQualified ideclQualified && not useQualifiedPost)
41+ (txt "qualified")
3842 space
3943 case ideclPkgQual of
4044 Nothing -> return ()
@@ -42,6 +46,9 @@ p_hsmodImport ImportDecl {..} = do
4246 space
4347 inci $ do
4448 located ideclName atom
49+ when
50+ (isImportDeclQualified ideclQualified && useQualifiedPost)
51+ (space >> txt "qualified")
4552 case ideclAs of
4653 Nothing -> return ()
4754 Just l -> do
@@ -56,62 +63,64 @@ p_hsmodImport ImportDecl {..} = do
5663 when hiding (txt "hiding")
5764 case ideclHiding of
5865 Nothing -> return ()
59− Just (_, (L _ xs)) -> do
66+ Just (_, L _ xs) -> do
6067 breakpoint
61− parens N . sitcc $ do
68+ parens N $ do
6269 layout <- getLayout
63− sep breakpoint (sitcc . located' (uncurry (p_lie layout))) (attachPositions xs)
70+ sep
71+ breakpoint
72+ (\(p, l) -> sitcc (located l (p_lie layout p)))
73+ (attachRelativePos xs)
6474 newline
65−p_hsmodImport (XImportDecl NoExt) = notImplemented "XImportDecl"
75+p_hsmodImport _ (XImportDecl x) = noExtCon x
6676
67−p_lie :: Layout -> (Int, Int) -> IE GhcPs -> R ()
68−p_lie encLayout (i, totalItems) = \case
69− IEVar NoExt l1 -> do
77+p_lie :: Layout -> RelativePos -> IE GhcPs -> R ()
78+p_lie encLayout relativePos = \case
79+ IEVar NoExtField l1 -> do
7080 located l1 p_ieWrappedName
7181 p_comma
72− IEThingAbs NoExt l1 -> do
82+ IEThingAbs NoExtField l1 -> do
7383 located l1 p_ieWrappedName
7484 p_comma
75− IEThingAll NoExt l1 -> do
85+ IEThingAll NoExtField l1 -> do
7686 located l1 p_ieWrappedName
7787 space
7888 txt "(..)"
7989 p_comma
80− IEThingWith NoExt l1 w xs _ -> sitcc $ do
90+ IEThingWith NoExtField l1 w xs _ -> sitcc $ do
8191 located l1 p_ieWrappedName
8292 breakpoint
8393 inci $ do
8494 let names :: [R ()]
8595 names = located' p_ieWrappedName <$> xs
86− parens N . sitcc
87− $ sep (comma >> breakpoint) sitcc
88− $ case w of
96+ parens N . sep commaDel sitcc $
97+ case w of
8998 NoIEWildcard -> names
9099 IEWildcard n ->
91100 let (before, after) = splitAt n names
92101 in before ++ [txt ".."] ++ after
93102 p_comma
94− IEModuleContents NoExt l1 -> do
103+ IEModuleContents NoExtField l1 -> do
95104 located l1 p_hsmodName
96105 p_comma
97− IEGroup NoExt n str -> do
98− unless (i == 0) newline
106+ IEGroup NoExtField n str -> do
107+ case relativePos of
108+ SinglePos -> return ()
109+ FirstPos -> return ()
110+ MiddlePos -> newline
111+ LastPos -> newline
99112 p_hsDocString (Asterisk n) False (noLoc str)
100− IEDoc NoExt str ->
113+ IEDoc NoExtField str ->
101114 p_hsDocString Pipe False (noLoc str)
102− IEDocNamed NoExt str -> p_hsDocName str
103− XIE NoExt -> notImplemented "XIE"
115+ IEDocNamed NoExtField str -> p_hsDocName str
116+ XIE x -> noExtCon x
104117 where
105118 p_comma =
106119 case encLayout of
107− SingleLine -> unless (i + 1 == totalItems) comma
120+ SingleLine ->
121+ case relativePos of
122+ SinglePos -> return ()
123+ FirstPos -> comma
124+ MiddlePos -> comma
125+ LastPos -> return ()
108126 MultiLine -> comma
109−
110−-- | Attach positions to 'Located' things in a list.
111−attachPositions ::
112− [Located a] ->
113− [Located ((Int, Int), a)]
114−attachPositions xs =
115− let f i (L l x) = L l ((i, n), x)
116− n = length xs
117− in zipWith f [0 ..] xs
src/Ormolu/Printer/Meat/Module.hs+35−15
@@ -9,9 +9,12 @@ module Ormolu.Printer.Meat.Module
99where
1010
1111import Control.Monad
12+import qualified Data.Text as T
1213import GHC
13−import Ormolu.Imports
14+import Ormolu.Imports (normalizeImports)
15+import Ormolu.Parser.CommentStream
1416import Ormolu.Parser.Pragma
17+import Ormolu.Parser.Shebang
1518import Ormolu.Printer.Combinators
1619import Ormolu.Printer.Comments
1720import Ormolu.Printer.Meat.Common
@@ -20,15 +23,31 @@ import Ormolu.Printer.Meat.Declaration.Warning
2023import Ormolu.Printer.Meat.ImportExport
2124import Ormolu.Printer.Meat.Pragma
2225
23−p_hsModule :: [Pragma] -> ParsedSource -> R ()
24−p_hsModule pragmas (L moduleSpan HsModule {..}) = do
25− -- NOTE If span of exports in multiline, the whole thing is multiline.
26− -- This is especially important because span of module itself always seems
27− -- to have length zero, so it's not reliable for layout selection.
28− let exportSpans = maybe [] (\(L s _) -> [s]) hsmodExports
29− deprecSpan = maybe [] (\(L s _) -> [s]) hsmodDeprecMessage
30− spans' = exportSpans ++ deprecSpan ++ [moduleSpan]
31− switchLayout spans' $ do
26+-- | Render a module.
27+p_hsModule ::
28+ -- | Stack header
29+ Maybe (RealLocated Comment) ->
30+ -- | Shebangs
31+ [Shebang] ->
32+ -- | Pragmas and the associated comments
33+ [([RealLocated Comment], Pragma)] ->
34+ -- | Whether to use postfix qualified imports
35+ Bool ->
36+ -- | AST to print
37+ HsModule GhcPs ->
38+ R ()
39+p_hsModule mstackHeader shebangs pragmas qualifiedPost HsModule {..} = do
40+ let deprecSpan = maybe [] (\(L s _) -> [s]) hsmodDeprecMessage
41+ exportSpans = maybe [] (\(L s _) -> [s]) hsmodExports
42+ switchLayout (deprecSpan <> exportSpans) $ do
43+ forM_ shebangs $ \(Shebang x) ->
44+ located x $ \shebang -> do
45+ txt (T.pack shebang)
46+ newline
47+ forM_ mstackHeader $ \(L spn comment) -> do
48+ spitCommentNow spn comment
49+ newline
50+ newline
3251 p_pragmas pragmas
3352 newline
3453 case hsmodName of
@@ -37,19 +56,20 @@ p_hsModule pragmas (L moduleSpan HsModule {..}) = do
3756 located hsmodName' $ \name -> do
3857 forM_ hsmodHaddockModHeader (p_hsDocString Pipe True)
3958 p_hsmodName name
59+ breakpoint
4060 forM_ hsmodDeprecMessage $ \w -> do
41− breakpoint
4261 located' p_moduleWarning w
62+ breakpoint
4363 case hsmodExports of
4464 Nothing -> return ()
45− Just hsmodExports' -> do
65+ Just l -> do
66+ located l $ \exports -> do
67+ inci (p_hsmodExports exports)
4668 breakpoint
47− inci (p_hsmodExports (unLoc hsmodExports'))
48− breakpoint
4969 txt "where"
5070 newline
5171 newline
52− forM_ (sortImports hsmodImports) (located' p_hsmodImport)
72+ forM_ (normalizeImports hsmodImports) (located' (p_hsmodImport qualifiedPost))
5373 newline
5474 switchLayout (getLoc <$> hsmodDecls) $ do
5575 p_hsDecls Free hsmodDecls
src/Ormolu/Printer/Meat/Pragma.hs+67−13
@@ -8,30 +8,84 @@ module Ormolu.Printer.Meat.Pragma
88 )
99where
1010
11−import qualified Data.Set as S
11+import Control.Monad
12+import Data.Char (isUpper)
13+import qualified Data.List as L
14+import Data.Maybe (listToMaybe)
1215import qualified Data.Text as T
16+import Ormolu.Parser.CommentStream
1317import Ormolu.Parser.Pragma (Pragma (..))
1418import Ormolu.Printer.Combinators
19+import Ormolu.Printer.Comments
20+import SrcLoc
1521
16−data PragmaTy = Language | OptionsGHC | OptionsHaddock
22+-- | Pragma classification.
23+data PragmaTy
24+ = Language LanguagePragmaClass
25+ | OptionsGHC
26+ | OptionsHaddock
1727 deriving (Eq, Ord)
1828
19−p_pragmas :: [Pragma] -> R ()
20−p_pragmas ps =
21− let prepare = concatMap $ \case
22− PragmaLanguage xs -> (Language,) <$> xs
23− PragmaOptionsGHC x -> [(OptionsGHC, x)]
24− PragmaOptionsHaddock x -> [(OptionsHaddock, x)]
25− in mapM_ (uncurry p_pragma) (S.toAscList . S.fromList . prepare $ ps)
29+-- | Language pragma classification.
30+--
31+-- The order in which language pragmas are put in the input sometimes
32+-- matters. This is because some language extensions can enable other
33+-- extensions, yet the extensions coming later in the list have the ability
34+-- to change it. So here we classify all extensions by assigning one of the
35+-- four groups to them. Then we only sort inside of the groups.
36+--
37+-- 'Ord' instance of this data type is what affects the sorting.
38+--
39+-- See also: <https://github.com/tweag/ormolu/issues/404>
40+data LanguagePragmaClass
41+ = -- | All other extensions
42+ Normal
43+ | -- | Extensions starting with "No"
44+ Disabling
45+ | -- | Extensions that should go after everything else
46+ Final
47+ deriving (Eq, Ord)
48+
49+-- | Print a collection of 'Pragma's with their associated comments.
50+p_pragmas :: [([RealLocated Comment], Pragma)] -> R ()
51+p_pragmas ps = do
52+ let prepare = L.sortOn snd . L.nub . concatMap analyze
53+ analyze = \case
54+ (cs, PragmaLanguage xs) ->
55+ let f x = (cs, (Language (classifyLanguagePragma x), x))
56+ in f <$> xs
57+ (cs, PragmaOptionsGHC x) -> [(cs, (OptionsGHC, x))]
58+ (cs, PragmaOptionsHaddock x) -> [(cs, (OptionsHaddock, x))]
59+ forM_ (prepare ps) $ \(cs, (pragmaTy, x)) ->
60+ p_pragma cs pragmaTy x
2661
27−p_pragma :: PragmaTy -> String -> R ()
28−p_pragma ty c = do
62+p_pragma :: [RealLocated Comment] -> PragmaTy -> String -> R ()
63+p_pragma comments ty x = do
64+ forM_ comments $ \(L l comment) -> do
65+ spitCommentNow l comment
66+ newline
2967 txt "{-# "
3068 txt $ case ty of
31− Language -> "LANGUAGE"
69+ Language _ -> "LANGUAGE"
3270 OptionsGHC -> "OPTIONS_GHC"
3371 OptionsHaddock -> "OPTIONS_HADDOCK"
3472 space
35− txt (T.pack c)
73+ txt (T.pack x)
3674 txt " #-}"
3775 newline
76+
77+-- | Classify a 'LanguagePragma'.
78+classifyLanguagePragma :: String -> LanguagePragmaClass
79+classifyLanguagePragma = \case
80+ "ImplicitPrelude" -> Final
81+ "CUSKs" -> Final
82+ str ->
83+ case splitAt 2 str of
84+ ("No", rest) ->
85+ case listToMaybe rest of
86+ Nothing -> Normal
87+ Just x ->
88+ if isUpper x
89+ then Disabling
90+ else Normal
91+ _ -> Normal
src/Ormolu/Printer/Meat/Type.hs+93−68
@@ -5,6 +5,7 @@
55-- | Rendering of types.
66module Ormolu.Printer.Meat.Type
77 ( p_hsType,
8+ p_hsTypePostDoc,
89 hasDocStrings,
910 p_hsContext,
1011 p_hsTyVarBndr,
@@ -23,15 +24,23 @@ import Ormolu.Printer.Operators
2324import Ormolu.Utils
2425
2526p_hsType :: HsType GhcPs -> R ()
26−p_hsType t = p_hsType' (hasDocStrings t) t
27+p_hsType t = p_hsType' (hasDocStrings t) PipeStyle t
2728
28−p_hsType' :: Bool -> HsType GhcPs -> R ()
29−p_hsType' multilineArgs = \case
30− HsForAllTy NoExt bndrs t -> do
31− p_forallBndrs p_hsTyVarBndr bndrs
29+p_hsTypePostDoc :: HsType GhcPs -> R ()
30+p_hsTypePostDoc t = p_hsType' (hasDocStrings t) CaretStyle t
31+
32+-- | How to render Haddocks associated with a type.
33+data TypeDocStyle
34+ = PipeStyle
35+ | CaretStyle
36+
37+p_hsType' :: Bool -> TypeDocStyle -> HsType GhcPs -> R ()
38+p_hsType' multilineArgs docStyle = \case
39+ HsForAllTy NoExtField visibility bndrs t -> do
40+ p_forallBndrs visibility p_hsTyVarBndr bndrs
3241 interArgBreak
33− p_hsType' multilineArgs (unLoc t)
34− HsQualTy NoExt qs t -> do
42+ p_hsTypeR (unLoc t)
43+ HsQualTy NoExtField qs t -> do
3544 located qs p_hsContext
3645 space
3746 txt "=>"
@@ -40,7 +49,7 @@ p_hsType' multilineArgs = \case
4049 HsQualTy {} -> p_hsTypeR (unLoc t)
4150 HsFunTy {} -> p_hsTypeR (unLoc t)
4251 _ -> located t p_hsTypeR
43− HsTyVar NoExt p n -> do
52+ HsTyVar NoExtField p n -> do
4453 case p of
4554 IsPromoted -> do
4655 txt "'"
@@ -49,10 +58,20 @@ p_hsType' multilineArgs = \case
4958 _ -> return ()
5059 NotPromoted -> return ()
5160 p_rdrName n
52− HsAppTy NoExt f x -> sitcc $ do
53− located f p_hsType
54− breakpoint
55− inci (located x p_hsType)
61+ HsAppTy NoExtField f x -> do
62+ let -- In order to format type applications with multiple parameters
63+ -- nicer, traverse the AST to gather the function and all the
64+ -- parameters together.
65+ gatherArgs f' knownArgs =
66+ case f' of
67+ L _ (HsAppTy _ l r) -> gatherArgs l (r : knownArgs)
68+ _ -> (f', knownArgs)
69+ (func, args) = gatherArgs f [x]
70+ switchLayout (getLoc f : fmap getLoc args) . sitcc $ do
71+ located func p_hsType
72+ breakpoint
73+ inci $
74+ sep breakpoint (located' p_hsType) args
5675 HsAppKindTy _ ty kd -> sitcc $ do
5776 -- The first argument is the location of the "@..." part. Not 100% sure,
5877 -- but I think we can ignore it as long as we use 'located' on both the
@@ -62,7 +81,7 @@ p_hsType' multilineArgs = \case
6281 inci $ do
6382 txt "@"
6483 located kd p_hsType
65− HsFunTy NoExt x y@(L _ y') -> do
84+ HsFunTy NoExtField x y@(L _ y') -> do
6685 located x p_hsType
6786 space
6887 txt "->"
@@ -70,43 +89,49 @@ p_hsType' multilineArgs = \case
7089 case y' of
7190 HsFunTy {} -> p_hsTypeR y'
7291 _ -> located y p_hsTypeR
73− HsListTy NoExt t ->
92+ HsListTy NoExtField t ->
7493 located t (brackets N . p_hsType)
75− HsTupleTy NoExt tsort xs ->
94+ HsTupleTy NoExtField tsort xs ->
7695 let parens' =
7796 case tsort of
7897 HsUnboxedTuple -> parensHash N
7998 HsBoxedTuple -> parens N
8099 HsConstraintTuple -> parens N
81100 HsBoxedOrConstraintTuple -> parens N
82− in parens' . sitcc $
83− sep (comma >> breakpoint) (sitcc . located' p_hsType) xs
84− HsSumTy NoExt xs ->
85− parensHash N . sitcc $
101+ in parens' $ sep commaDel (sitcc . located' p_hsType) xs
102+ HsSumTy NoExtField xs ->
103+ parensHash N $
86104 sep (txt "|" >> breakpoint) (sitcc . located' p_hsType) xs
87− HsOpTy NoExt x op y -> sitcc $ do
88− let opTree = OpBranch (tyOpTree x) op (tyOpTree y)
89− in p_tyOpTree (reassociateOpTree Just opTree)
90− HsParTy NoExt t ->
105+ HsOpTy NoExtField x op y ->
106+ sitcc $
107+ let opTree = OpBranch (tyOpTree x) op (tyOpTree y)
108+ in p_tyOpTree (reassociateOpTree Just opTree)
109+ HsParTy NoExtField t ->
91110 parens N (located t p_hsType)
92− HsIParamTy NoExt n t -> sitcc $ do
111+ HsIParamTy NoExtField n t -> sitcc $ do
93112 located n atom
94113 space
95114 txt "::"
96115 breakpoint
97116 inci (located t p_hsType)
98− HsStarTy NoExt _ -> txt "*"
99− HsKindSig NoExt t k -> sitcc $ do
117+ HsStarTy NoExtField _ -> txt "*"
118+ HsKindSig NoExtField t k -> sitcc $ do
100119 located t p_hsType
101− space -- FIXME
102− txt "::"
103120 space
121+ txt "::"
122+ breakpoint
104123 inci (located k p_hsType)
105− HsSpliceTy NoExt splice -> p_hsSplice splice
106− HsDocTy NoExt t str -> do
107− p_hsDocString Pipe True str
108− located t p_hsType
109− HsBangTy NoExt (HsSrcBang _ u s) t -> do
124+ HsSpliceTy NoExtField splice -> p_hsSplice splice
125+ HsDocTy NoExtField t str ->
126+ case docStyle of
127+ PipeStyle -> do
128+ p_hsDocString Pipe True str
129+ located t p_hsType
130+ CaretStyle -> do
131+ located t p_hsType
132+ newline
133+ p_hsDocString Caret False str
134+ HsBangTy NoExtField (HsSrcBang _ u s) t -> do
110135 case u of
111136 SrcUnpack -> txt "{-# UNPACK #-}" >> space
112137 SrcNoUnpack -> txt "{-# NOUNPACK #-}" >> space
@@ -116,9 +141,9 @@ p_hsType' multilineArgs = \case
116141 SrcStrict -> txt "!"
117142 NoSrcStrict -> return ()
118143 located t p_hsType
119− HsRecTy NoExt fields ->
144+ HsRecTy NoExtField fields ->
120145 p_conDeclFields fields
121− HsExplicitListTy NoExt p xs -> do
146+ HsExplicitListTy NoExtField p xs -> do
122147 case p of
123148 IsPromoted -> txt "'"
124149 NotPromoted -> return ()
@@ -126,39 +151,39 @@ p_hsType' multilineArgs = \case
126151 -- If both this list itself and the first element is promoted,
127152 -- we need to put a space in between or it fails to parse.
128153 case (p, xs) of
129− (IsPromoted, ((L _ t) : _)) | isPromoted t -> space
154+ (IsPromoted, L _ t : _) | isPromoted t -> space
130155 _ -> return ()
131− sitcc $ sep (comma >> breakpoint) (sitcc . located' p_hsType) xs
132− HsExplicitTupleTy NoExt xs -> do
156+ sep commaDel (sitcc . located' p_hsType) xs
157+ HsExplicitTupleTy NoExtField xs -> do
133158 txt "'"
134159 parens N $ do
135160 case xs of
136− ((L _ t) : _) | isPromoted t -> space
161+ L _ t : _ | isPromoted t -> space
137162 _ -> return ()
138− sep (comma >> breakpoint) (located' p_hsType) xs
139− HsTyLit NoExt t ->
163+ sep commaDel (located' p_hsType) xs
164+ HsTyLit NoExtField t ->
140165 case t of
141166 HsStrTy (SourceText s) _ -> p_stringLit s
142167 a -> atom a
143− HsWildCardTy NoExt -> txt "_"
168+ HsWildCardTy NoExtField -> txt "_"
144169 XHsType (NHsCoreTy t) -> atom t
145170 where
146171 isPromoted = \case
147172 HsTyVar _ IsPromoted _ -> True
148− HsExplicitListTy _ _ _ -> True
149− HsExplicitTupleTy _ _ -> True
173+ HsExplicitTupleTy {} -> True
174+ HsExplicitListTy {} -> True
150175 _ -> False
151176 interArgBreak =
152177 if multilineArgs
153178 then newline
154179 else breakpoint
155− p_hsTypeR = p_hsType' multilineArgs
180+ p_hsTypeR = p_hsType' multilineArgs docStyle
156181
157182-- | Return 'True' if at least one argument in 'HsType' has a doc string
158183-- attached to it.
159184hasDocStrings :: HsType GhcPs -> Bool
160185hasDocStrings = \case
161− HsDocTy _ _ _ -> True
186+ HsDocTy {} -> True
162187 HsFunTy _ (L _ x) (L _ y) -> hasDocStrings x || hasDocStrings y
163188 _ -> False
164189
@@ -166,61 +191,61 @@ p_hsContext :: HsContext GhcPs -> R ()
166191p_hsContext = \case
167192 [] -> txt "()"
168193 [x] -> located x p_hsType
169− xs ->
170− parens N . sitcc $
171− sep (comma >> breakpoint) (sitcc . located' p_hsType) xs
194+ xs -> parens N $ sep commaDel (sitcc . located' p_hsType) xs
172195
173196p_hsTyVarBndr :: HsTyVarBndr GhcPs -> R ()
174197p_hsTyVarBndr = \case
175− UserTyVar NoExt x ->
198+ UserTyVar NoExtField x ->
176199 p_rdrName x
177− KindedTyVar NoExt l k -> parens N $ do
200+ KindedTyVar NoExtField l k -> parens N $ do
178201 located l atom
179202 space
180203 txt "::"
181204 breakpoint
182205 inci (located k p_hsType)
183− XTyVarBndr NoExt -> notImplemented "XTyVarBndr"
206+ XTyVarBndr x -> noExtCon x
184207
185208-- | Render several @forall@-ed variables.
186−p_forallBndrs :: Data a => (a -> R ()) -> [Located a] -> R ()
187−p_forallBndrs _ [] = return ()
188−p_forallBndrs p tyvars =
209+p_forallBndrs :: Data a => ForallVisFlag -> (a -> R ()) -> [Located a] -> R ()
210+p_forallBndrs ForallInvis _ [] = txt "forall."
211+p_forallBndrs ForallVis _ [] = txt "forall ->"
212+p_forallBndrs vis p tyvars =
189213 switchLayout (getLoc <$> tyvars) $ do
190214 txt "forall"
191215 breakpoint
192216 inci $ do
193217 sitcc $ sep breakpoint (sitcc . located' p) tyvars
194− txt "."
218+ case vis of
219+ ForallInvis -> txt "."
220+ ForallVis -> space >> txt "->"
195221
196222p_conDeclFields :: [LConDeclField GhcPs] -> R ()
197223p_conDeclFields xs =
198− braces N . sitcc $
199− sep (comma >> breakpoint) (sitcc . located' p_conDeclField) xs
224+ braces N $ sep commaDel (sitcc . located' p_conDeclField) xs
200225
201226p_conDeclField :: ConDeclField GhcPs -> R ()
202227p_conDeclField ConDeclField {..} = do
203228 mapM_ (p_hsDocString Pipe True) cd_fld_doc
204229 sitcc $
205230 sep
206− (comma >> breakpoint)
231+ commaDel
207232 (located' (p_rdrName . rdrNameFieldOcc))
208233 cd_fld_names
209234 space
210235 txt "::"
211236 breakpoint
212237 sitcc . inci $ p_hsType (unLoc cd_fld_type)
213−p_conDeclField (XConDeclField NoExt) = notImplemented "XConDeclField"
238+p_conDeclField (XConDeclField x) = noExtCon x
214239
215240tyOpTree :: LHsType GhcPs -> OpTree (LHsType GhcPs) (Located RdrName)
216−tyOpTree (L _ (HsOpTy NoExt l op r)) =
241+tyOpTree (L _ (HsOpTy NoExtField l op r)) =
217242 OpBranch (tyOpTree l) op (tyOpTree r)
218243tyOpTree n = OpNode n
219244
220245p_tyOpTree :: OpTree (LHsType GhcPs) (Located RdrName) -> R ()
221246p_tyOpTree (OpNode n) = located n p_hsType
222247p_tyOpTree (OpBranch l op r) = do
223− switchLayout [opTreeLoc l] $ do
248+ switchLayout [opTreeLoc l] $
224249 p_tyOpTree l
225250 breakpoint
226251 inci . switchLayout [opTreeLoc r] $ do
@@ -234,17 +259,17 @@ p_tyOpTree (OpBranch l op r) = do
234259tyVarsToTypes :: LHsQTyVars GhcPs -> [LHsType GhcPs]
235260tyVarsToTypes = \case
236261 HsQTvs {..} -> fmap tyVarToType <$> hsq_explicit
237− XLHsQTyVars {} -> notImplemented "XLHsQTyVars"
262+ XLHsQTyVars x -> noExtCon x
238263
239264tyVarToType :: HsTyVarBndr GhcPs -> HsType GhcPs
240265tyVarToType = \case
241− UserTyVar NoExt tvar -> HsTyVar NoExt NotPromoted tvar
242− KindedTyVar NoExt tvar kind ->
266+ UserTyVar NoExtField tvar -> HsTyVar NoExtField NotPromoted tvar
267+ KindedTyVar NoExtField tvar kind ->
243268 -- Note: we always add parentheses because for whatever reason GHC does
244269 -- not use HsParTy for left-hand sides of declarations. Please see
245270 -- <https://gitlab.haskell.org/ghc/ghc/issues/17404>. This is fine as
246271 -- long as 'tyVarToType' does not get applied to right-hand sides of
247272 -- declarations.
248− HsParTy NoExt $ noLoc $
249− HsKindSig NoExt (noLoc (HsTyVar NoExt NotPromoted tvar)) kind
250− XTyVarBndr {} -> notImplemented "XTyVarBndr"
273+ HsParTy NoExtField . noLoc $
274+ HsKindSig NoExtField (noLoc (HsTyVar NoExtField NotPromoted tvar)) kind
275+ XTyVarBndr x -> noExtCon x
src/Ormolu/Printer/Operators.hs+57−62
@@ -10,14 +10,14 @@ module Ormolu.Printer.Operators
1010 )
1111where
1212
13−import BasicTypes (Fixity (..), SourceText (NoSourceText), compareFixity, defaultFixity)
1413import Data.Function (on)
15−import Data.List
16−import Data.Maybe (fromMaybe)
14+import qualified Data.List as L
15+import Data.Map.Strict (Map)
16+import qualified Data.Map.Strict as M
17+import Data.Maybe (fromMaybe, mapMaybe)
1718import GHC
18−import OccName (mkVarOcc)
19−import RdrName (mkRdrUnqual)
20−import SrcLoc (combineSrcSpans)
19+import OccName (occNameString)
20+import Ormolu.Utils (unSrcSpan)
2121
2222-- | Intermediate representation of operator trees. It has two type
2323-- parameters: @ty@ is the type of sub-expressions, while @op@ is the type
@@ -57,7 +57,7 @@ reassociateOpTree getOpName opTree =
5757reassociateOpTreeWith ::
5858 forall ty op.
5959 -- | Fixity map for operators
60− [(RdrName, Fixity)] ->
60+ Map String Fixity ->
6161 -- | How to get the name of an operator
6262 (op -> Maybe RdrName) ->
6363 -- | Original 'OpTree'
@@ -68,13 +68,13 @@ reassociateOpTreeWith fixityMap getOpName = go
6868 where
6969 fixityOf :: op -> Fixity
7070 fixityOf op = fromMaybe defaultFixity $ do
71− opName <- getOpName op
72− lookup opName fixityMap
71+ s <- occNameString . rdrNameOcc <$> getOpName op
72+ M.lookup s fixityMap
7373 -- Here, left branch is already associated and the root alongside with
7474 -- the right branch is right-associated. This function picks up one item
7575 -- from the right and inserts it correctly to the left.
7676 --
77− -- Also, we are using the 'compareFixity' function which returns if the
77+ -- Also, we are using the 'compareFixity' function which tells if the
7878 -- expression should associate to right.
7979 go :: OpTree ty op -> OpTree ty op
8080 -- base cases
@@ -94,6 +94,16 @@ reassociateOpTreeWith fixityMap getOpName = go
9494 then go $ OpBranch (OpBranch l op (go $ OpBranch r op' l')) op'' r'
9595 else go $ OpBranch (OpBranch (OpBranch l op r) op' l') op'' r'
9696
97+-- | A score assigned to an operator.
98+data Score
99+ = -- | The operator was placed at the beginning of a line
100+ AtBeginning Int
101+ | -- | The operator was placed at the end of a line
102+ AtEnd
103+ | -- | The operator was placed in between arguments on a single line
104+ InBetween
105+ deriving (Eq, Ord)
106+
97107-- | Build a map of inferred 'Fixity's from an 'OpTree'.
98108buildFixityMap ::
99109 forall ty op.
@@ -102,23 +112,26 @@ buildFixityMap ::
102112 -- | Operator tree
103113 OpTree (Located ty) (Located op) ->
104114 -- | Fixity map
105− [(RdrName, Fixity)]
115+ Map String Fixity
106116buildFixityMap getOpName opTree =
107− concatMap (\(i, ns) -> map (\(n, _) -> (n, fixity i InfixL)) ns)
108− . zip [0 ..]
109− . groupBy (doubleWithinEps 0.00001 `on` snd)
110− . (overrides ++)
111− . avgScores
117+ addOverrides
118+ . M.fromList
119+ . concatMap (\(i, ns) -> map (\(n, _) -> (n, fixity i InfixL)) ns)
120+ . zip [2 ..]
121+ . L.groupBy ((==) `on` snd)
122+ . selectScores
112123 $ score opTree
113124 where
114− -- Add a special case for ($), since it is pretty unlikely for someone
115− -- to override it.
116− overrides :: [(RdrName, Double)]
117− overrides =
118− [ (mkRdrUnqual $ mkVarOcc "$", -1)
119− ]
120− -- Assign scores to operators based on their location in the source.
121− score :: OpTree (Located ty) (Located op) -> [(RdrName, Double)]
125+ addOverrides :: Map String Fixity -> Map String Fixity
126+ addOverrides m =
127+ M.fromList
128+ [ ("$", fixity 0 InfixR),
129+ (":", fixity 1 InfixR),
130+ (".", fixity 100 InfixL)
131+ ]
132+ `M.union` m
133+ fixity = Fixity NoSourceText
134+ score :: OpTree (Located ty) (Located op) -> [(String, Score)]
122135 score (OpNode _) = []
123136 score (OpBranch l o r) = fromMaybe (score r) $ do
124137 -- If we fail to get any of these, 'defaultFixity' will be used by
@@ -128,39 +141,27 @@ buildFixityMap getOpName opTree =
128141 oe <- srcSpanEndLine <$> unSrcSpan (getLoc o) -- operator end
129142 rb <- srcSpanStartLine <$> unSrcSpan (opTreeLoc r) -- right begin
130143 oc <- srcSpanStartCol <$> unSrcSpan (getLoc o) -- operator column
131− opName <- getOpName (unLoc o)
132− let s =
133− if le < ob
134− then-- if the operator is in the beginning of a line, assign
135− -- a score relative to its column within range [0, 1).
136− fromIntegral oc / fromIntegral (maxCol + 1)
137− else-- if the operator is in the end of the line, assign the
138− -- score 1.
139−
140− if oe < rb
141− then 1
142− else 2 -- otherwise, assign a high score.
144+ opName <- occNameString . rdrNameOcc <$> getOpName (unLoc o)
145+ let s
146+ | le < ob = AtBeginning oc
147+ | oe < rb = AtEnd
148+ | otherwise = InBetween
143149 return $ (opName, s) : score r
144− avgScores :: [(RdrName, Double)] -> [(RdrName, Double)]
145− avgScores =
146− sortOn snd
147− . map (\xs@((n, _) : _) -> (n, avg $ map snd xs))
148− . groupBy ((==) `on` fst)
149− . sort
150− avg :: [Double] -> Double
151− avg i = sum i / fromIntegral (length i)
152− -- The start column of the rightmost operator.
153− maxCol = go opTree
154− where
155− go (OpNode (L _ _)) = 0
156− go (OpBranch l (L o _) r) =
157− maximum
158− [ go l,
159− maybe 0 srcSpanStartCol (unSrcSpan o),
160− go r
161− ]
162− unSrcSpan (RealSrcSpan r) = Just r
163− unSrcSpan (UnhelpfulSpan _) = Nothing
150+ selectScores :: [(String, Score)] -> [(String, Score)]
151+ selectScores =
152+ L.sortOn snd
153+ . mapMaybe
154+ ( \case
155+ [] -> Nothing
156+ xs@((n, _) : _) -> Just (n, selectScore $ map snd xs)
157+ )
158+ . L.groupBy ((==) `on` fst)
159+ . L.sort
160+ selectScore :: [Score] -> Score
161+ selectScore xs =
162+ case filter (/= InBetween) xs of
163+ [] -> InBetween
164+ xs' -> maximum xs'
164165
165166----------------------------------------------------------------------------
166167-- Helpers
@@ -174,9 +175,3 @@ normalizeOpTree (OpBranch (OpNode l) lop r) =
174175 OpBranch (OpNode l) lop (normalizeOpTree r)
175176normalizeOpTree (OpBranch (OpBranch l' lop' r') lop r) =
176177 normalizeOpTree (OpBranch l' lop' (OpBranch r' lop r))
177−
178−fixity :: Int -> FixityDirection -> Fixity
179−fixity = Fixity NoSourceText
180−
181−doubleWithinEps :: Double -> Double -> Double -> Bool
182−doubleWithinEps eps a b = abs (a - b) < eps
src/Ormolu/Printer/SpanStream.hs+1−1
@@ -36,7 +36,7 @@ mkSpanStream a =
3636 $ everything mappend (const mempty `ext2Q` queryLocated) a
3737 where
3838 queryLocated ::
39− (Data e0, Data e1) =>
39+ (Data e0) =>
4040 GenLocated e0 e1 ->
4141 DList RealSrcSpan
4242 queryLocated (L mspn _) =
src/Ormolu/Processing/Common.hsnew file+27
@@ -0,0 +1,27 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+-- | Common definitions for pre- and post- processing.
4+module Ormolu.Processing.Common
5+ ( OrmoluState (..),
6+ startDisabling,
7+ endDisabling,
8+ )
9+where
10+
11+import Data.String (IsString (..))
12+
13+-- | Ormolu state.
14+data OrmoluState
15+ = -- | Enabled
16+ OrmoluEnabled
17+ | -- | Disabled
18+ OrmoluDisabled
19+ deriving (Eq, Show)
20+
21+-- | Marker for the beginning of the region where Ormolu should be disabled.
22+startDisabling :: IsString s => s
23+startDisabling = "{- ORMOLU_DISABLE_START"
24+
25+-- | Marker for the end of the region where Ormolu should be disabled.
26+endDisabling :: IsString s => s
27+endDisabling = "ORMOLU_DISABLE_END -}"
src/Ormolu/Processing/Cpp.hsnew file+70
@@ -0,0 +1,70 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+-- | Support for CPP.
4+module Ormolu.Processing.Cpp
5+ ( State (..),
6+ processLine,
7+ unmaskLine,
8+ )
9+where
10+
11+import Control.Monad
12+import Data.Char (isSpace)
13+import qualified Data.List as L
14+import Data.Maybe (isJust)
15+import Data.String
16+import Data.Text (Text)
17+import qualified Data.Text as T
18+
19+-- | State of the CPP processor.
20+data State
21+ = -- | Outside of CPP directives
22+ Outside
23+ | -- | In a conditional expression
24+ InConditional
25+ | -- | In a continuation (after @\\@)
26+ InContinuation
27+ deriving (Eq, Show)
28+
29+-- | Automatically mask the line when needed and update the 'State'.
30+processLine :: String -> State -> (String, State)
31+processLine line state
32+ | for "define " = (masked, state')
33+ | for "include " = (masked, state')
34+ | for "undef " = (masked, state')
35+ | for "ifdef " = (masked, InConditional)
36+ | for "ifndef " = (masked, InConditional)
37+ | for "if " = (masked, InConditional)
38+ | for "else" = (masked, InConditional)
39+ | for "elif" = (masked, InConditional)
40+ | for "endif" = (masked, state')
41+ | otherwise =
42+ case state of
43+ Outside -> (line, Outside)
44+ InConditional -> (masked, InConditional)
45+ InContinuation -> (masked, state')
46+ where
47+ for directive = isJust $ do
48+ s <- dropWhile isSpace <$> L.stripPrefix "#" line
49+ void (L.stripPrefix directive s)
50+ masked = maskLine line
51+ state' =
52+ if "\\" `L.isSuffixOf` line
53+ then InContinuation
54+ else Outside
55+
56+-- | Mask the given line.
57+maskLine :: String -> String
58+maskLine x = maskPrefix ++ x
59+
60+-- | If the given line is masked, unmask it. Otherwise return the line
61+-- unchanged.
62+unmaskLine :: Text -> Text
63+unmaskLine x =
64+ case T.stripPrefix maskPrefix (T.stripStart x) of
65+ Nothing -> x
66+ Just x' -> x'
67+
68+-- | Mask prefix for CPP.
69+maskPrefix :: IsString s => s
70+maskPrefix = "-- ORMOLU_CPP_MASK"
src/Ormolu/Processing/Postprocess.hsnew file+31
@@ -0,0 +1,31 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ViewPatterns #-}
3+
4+-- | Postprocessing for the results of printing.
5+module Ormolu.Processing.Postprocess
6+ ( postprocess,
7+ )
8+where
9+
10+import Data.Text (Text)
11+import qualified Data.Text as T
12+import Ormolu.Processing.Common
13+import qualified Ormolu.Processing.Cpp as Cpp
14+
15+-- | Postprocess output of the formatter.
16+postprocess ::
17+ -- | Desired indentation level
18+ Int ->
19+ -- | Input to process
20+ Text ->
21+ Text
22+postprocess indent =
23+ T.unlines
24+ . fmap indentLine
25+ . fmap Cpp.unmaskLine
26+ . filter (not . magicComment)
27+ . T.lines
28+ where
29+ magicComment (T.stripStart -> x) =
30+ x == startDisabling || x == endDisabling
31+ indentLine x = T.replicate indent " " <> x
src/Ormolu/Processing/Preprocess.hsnew file+149
@@ -0,0 +1,149 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE RecordWildCards #-}
4+
5+-- | Preprocessing for input source code.
6+module Ormolu.Processing.Preprocess
7+ ( preprocess,
8+ )
9+where
10+
11+import Control.Monad
12+import Data.Char (isSpace)
13+import qualified Data.List as L
14+import Data.Maybe (isJust, maybeToList)
15+import FastString
16+import Ormolu.Config (RegionDeltas (..))
17+import Ormolu.Parser.Shebang (isShebang)
18+import Ormolu.Processing.Common
19+import qualified Ormolu.Processing.Cpp as Cpp
20+import SrcLoc
21+
22+-- | Transform given input possibly returning comments extracted from it.
23+-- This handles LINE pragmas, CPP, shebangs, and the magic comments for
24+-- enabling\/disabling of Ormolu.
25+preprocess ::
26+ -- | File name, just to use in the spans
27+ FilePath ->
28+ -- | Input to process
29+ String ->
30+ -- | Region deltas
31+ RegionDeltas ->
32+ -- | Literal prefix, pre-processed input, literal suffix, extra comments
33+ (String, String, String, [Located String])
34+preprocess path input RegionDeltas {..} =
35+ go 1 OrmoluEnabled Cpp.Outside id id regionLines
36+ where
37+ (prefixLines, otherLines) = splitAt regionPrefixLength (lines input)
38+ (regionLines, suffixLines) =
39+ let regionLength = length otherLines - regionSuffixLength
40+ in splitAt regionLength otherLines
41+ go !n ormoluState cppState inputSoFar csSoFar = \case
42+ [] ->
43+ let input' = unlines (inputSoFar [])
44+ in ( unlines prefixLines,
45+ case ormoluState of
46+ OrmoluEnabled -> input'
47+ OrmoluDisabled -> input' ++ endDisabling,
48+ unlines suffixLines,
49+ csSoFar []
50+ )
51+ (x : xs) ->
52+ let (x', ormoluState', cppState', cs) =
53+ processLine path n ormoluState cppState x
54+ in go
55+ (n + 1)
56+ ormoluState'
57+ cppState'
58+ (inputSoFar . (x' :))
59+ (csSoFar . (maybeToList cs ++))
60+ xs
61+
62+-- | Transform a given line possibly returning a comment extracted from it.
63+processLine ::
64+ -- | File name, just to use in the spans
65+ FilePath ->
66+ -- | Line number of this line
67+ Int ->
68+ -- | Whether Ormolu is currently enabled
69+ OrmoluState ->
70+ -- | CPP state
71+ Cpp.State ->
72+ -- | The actual line
73+ String ->
74+ -- | Adjusted line and possibly a comment extracted from it
75+ (String, OrmoluState, Cpp.State, Maybe (Located String))
76+processLine path n ormoluState Cpp.Outside line
77+ | "{-# LINE" `L.isPrefixOf` line =
78+ let (pragma, res) = getPragma line
79+ size = length pragma
80+ ss = mkSrcSpan (mkSrcLoc' 1) (mkSrcLoc' (size + 1))
81+ in (res, ormoluState, Cpp.Outside, Just (L ss pragma))
82+ | isOrmoluEnable line =
83+ case ormoluState of
84+ OrmoluEnabled ->
85+ (enableMarker, OrmoluEnabled, Cpp.Outside, Nothing)
86+ OrmoluDisabled ->
87+ (endDisabling ++ enableMarker, OrmoluEnabled, Cpp.Outside, Nothing)
88+ | isOrmoluDisable line =
89+ case ormoluState of
90+ OrmoluEnabled ->
91+ (disableMarker ++ startDisabling, OrmoluDisabled, Cpp.Outside, Nothing)
92+ OrmoluDisabled ->
93+ (disableMarker, OrmoluDisabled, Cpp.Outside, Nothing)
94+ | isShebang line =
95+ let ss = mkSrcSpan (mkSrcLoc' 1) (mkSrcLoc' (length line))
96+ in ("", ormoluState, Cpp.Outside, Just (L ss line))
97+ | otherwise =
98+ let (line', cppState') = Cpp.processLine line Cpp.Outside
99+ in (line', ormoluState, cppState', Nothing)
100+ where
101+ mkSrcLoc' = mkSrcLoc (mkFastString path) n
102+processLine _ _ ormoluState cppState line =
103+ let (line', cppState') = Cpp.processLine line cppState
104+ in (line', ormoluState, cppState', Nothing)
105+
106+-- | Take a line pragma and output its replacement (where line pragma is
107+-- replaced with spaces) and the contents of the pragma itself.
108+getPragma ::
109+ -- | Pragma line to analyze
110+ String ->
111+ -- | Contents of the pragma and its replacement line
112+ (String, String)
113+getPragma [] = error "Ormolu.Preprocess.getPragma: input must not be empty"
114+getPragma s@(x : xs)
115+ | "#-}" `L.isPrefixOf` s = ("#-}", " " ++ drop 3 s)
116+ | otherwise =
117+ let (prag, remline) = getPragma xs
118+ in (x : prag, ' ' : remline)
119+
120+-- | Canonical enable marker.
121+enableMarker :: String
122+enableMarker = "{- ORMOLU_ENABLE -}"
123+
124+-- | Canonical disable marker.
125+disableMarker :: String
126+disableMarker = "{- ORMOLU_DISABLE -}"
127+
128+-- | Return 'True' if the given string is an enabling marker.
129+isOrmoluEnable :: String -> Bool
130+isOrmoluEnable = magicComment "ORMOLU_ENABLE"
131+
132+-- | Return 'True' if the given string is a disabling marker.
133+isOrmoluDisable :: String -> Bool
134+isOrmoluDisable = magicComment "ORMOLU_DISABLE"
135+
136+-- | Construct a function for whitespace-insensitive matching of string.
137+magicComment ::
138+ -- | What to expect
139+ String ->
140+ -- | String to test
141+ String ->
142+ -- | Whether or not the two strings watch
143+ Bool
144+magicComment expected s0 = isJust $ do
145+ let trim = dropWhile isSpace
146+ s1 <- trim <$> L.stripPrefix "{-" (trim s0)
147+ s2 <- trim <$> L.stripPrefix expected s1
148+ s3 <- L.stripPrefix "-}" s2
149+ guard (all isSpace s3)
src/Ormolu/Utils.hs+83−9
@@ -1,41 +1,66 @@
11{-# LANGUAGE LambdaCase #-}
22{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE ViewPatterns #-}
34
45-- | Random utilities used by the code.
56module Ormolu.Utils
6− ( combineSrcSpans',
7− isModule,
7+ ( RelativePos (..),
8+ attachRelativePos,
9+ combineSrcSpans',
810 notImplemented,
911 showOutputable,
1012 splitDocString,
1113 typeArgToType,
14+ unSrcSpan,
15+ incSpanLine,
16+ separatedByBlank,
17+ separatedByBlankNE,
18+ onTheSameLine,
19+ removeIndentation,
1220 )
1321where
1422
15−import Data.Data (Data, showConstr, toConstr)
23+import Data.Char (isSpace)
1624import Data.List (dropWhileEnd)
1725import Data.List.NonEmpty (NonEmpty (..))
26+import qualified Data.List.NonEmpty as NE
27+import Data.Maybe (fromMaybe)
1828import Data.Text (Text)
1929import qualified Data.Text as T
2030import GHC
21−import HsDoc (HsDocString, unpackHDS)
31+import GHC.DynFlags (baseDynFlags)
2232import qualified Outputable as GHC
2333
34+-- | Relative positions in a list.
35+data RelativePos
36+ = SinglePos
37+ | FirstPos
38+ | MiddlePos
39+ | LastPos
40+ deriving (Eq, Show)
41+
42+-- | Attach 'RelativePos'es to elements of a given list.
43+attachRelativePos :: [a] -> [(RelativePos, a)]
44+attachRelativePos = \case
45+ [] -> []
46+ [x] -> [(SinglePos, x)]
47+ (x : xs) -> (FirstPos, x) : markLast xs
48+ where
49+ markLast [] = []
50+ markLast [x] = [(LastPos, x)]
51+ markLast (x : xs) = (MiddlePos, x) : markLast xs
52+
2453-- | Combine all source spans from the given list.
2554combineSrcSpans' :: NonEmpty SrcSpan -> SrcSpan
2655combineSrcSpans' (x :| xs) = foldr combineSrcSpans x xs
2756
28−-- | Return 'True' if given element of AST is module.
29−isModule :: Data a => a -> Bool
30−isModule x = showConstr (toConstr x) == "HsModule"
31−
3257-- | Placeholder for things that are not yet implemented.
3358notImplemented :: String -> a
3459notImplemented msg = error $ "not implemented yet: " ++ msg
3560
3661-- | Pretty-print an 'GHC.Outputable' thing.
3762showOutputable :: GHC.Outputable o => o -> String
38−showOutputable = GHC.showSDocUnsafe . GHC.ppr
63+showOutputable = GHC.showSDoc baseDynFlags . GHC.ppr
3964
4065-- | Split and normalize a doc string. The result is a list of lines that
4166-- make up the comment.
@@ -74,8 +99,57 @@ splitDocString docStr =
7499 then dropSpace <$> xs
75100 else xs
76101
102+-- | Get 'LHsType' out of 'LHsTypeArg'.
77103typeArgToType :: LHsTypeArg p -> LHsType p
78104typeArgToType = \case
79105 HsValArg tm -> tm
80106 HsTypeArg _ ty -> ty
81107 HsArgPar _ -> notImplemented "HsArgPar"
108+
109+-- | Get 'RealSrcSpan' out of 'SrcSpan' if the span is “helpful”.
110+unSrcSpan :: SrcSpan -> Maybe RealSrcSpan
111+unSrcSpan = \case
112+ RealSrcSpan r -> Just r
113+ UnhelpfulSpan _ -> Nothing
114+
115+-- | Increment line number in a 'SrcSpan'.
116+incSpanLine :: Int -> SrcSpan -> SrcSpan
117+incSpanLine i = \case
118+ RealSrcSpan s ->
119+ let start = realSrcSpanStart s
120+ end = realSrcSpanEnd s
121+ incLine x =
122+ let file = srcLocFile x
123+ line = srcLocLine x
124+ col = srcLocCol x
125+ in mkRealSrcLoc file (line + i) col
126+ in RealSrcSpan (mkRealSrcSpan (incLine start) (incLine end))
127+ UnhelpfulSpan x -> UnhelpfulSpan x
128+
129+-- | Do two declarations have a blank between them?
130+separatedByBlank :: (a -> SrcSpan) -> a -> a -> Bool
131+separatedByBlank loc a b =
132+ fromMaybe False $ do
133+ endA <- srcSpanEndLine <$> unSrcSpan (loc a)
134+ startB <- srcSpanStartLine <$> unSrcSpan (loc b)
135+ pure (startB - endA >= 2)
136+
137+-- | Do two declaration groups have a blank between them?
138+separatedByBlankNE :: (a -> SrcSpan) -> NonEmpty a -> NonEmpty a -> Bool
139+separatedByBlankNE loc a b = separatedByBlank loc (NE.last a) (NE.head b)
140+
141+-- | Return 'True' if one span ends on the same line the second one starts.
142+onTheSameLine :: SrcSpan -> SrcSpan -> Bool
143+onTheSameLine a b =
144+ isOneLineSpan (mkSrcSpan (srcSpanEnd a) (srcSpanStart b))
145+
146+-- | Remove indentation from a given 'String'. Return the input with
147+-- indentation removed and the detected indentation level.
148+removeIndentation :: String -> (String, Int)
149+removeIndentation (lines -> xs) = (unlines (drop n <$> xs), n)
150+ where
151+ n = minimum (getIndent <$> xs)
152+ getIndent y =
153+ if all isSpace y
154+ then 0
155+ else length (takeWhile isSpace y)
tests/Ormolu/Parser/PragmaSpec.hs+1−1
@@ -4,7 +4,7 @@ import Ormolu.Parser.Pragma
44import Test.Hspec
55
66spec :: Spec
7−spec = do
7+spec =
88 describe "parsePragma" $ do
99 stdTest "{-# LANGUAGE Foo #-}" (Just (PragmaLanguage ["Foo"]))
1010 stdTest "{-# language Foo #-}" (Just (PragmaLanguage ["Foo"]))
tests/Ormolu/PrinterSpec.hs+7−10
@@ -1,9 +1,6 @@
11{-# LANGUAGE TemplateHaskell #-}
22
3−module Ormolu.PrinterSpec
4− ( spec,
5− )
6−where
3+module Ormolu.PrinterSpec (spec) where
74
85import Control.Exception
96import Control.Monad
@@ -15,7 +12,7 @@ import qualified Data.Text.IO as T
1512import Ormolu
1613import Path
1714import Path.IO
18−import System.FilePath (addExtension, dropExtensions, splitExtensions)
15+import qualified System.FilePath as F
1916import Test.Hspec
2017
2118spec :: Spec
@@ -53,19 +50,19 @@ locateExamples =
5350isInput :: Path Rel File -> Bool
5451isInput path =
5552 let s = fromRelFile path
56− (s', exts) = splitExtensions s
53+ (s', exts) = F.splitExtensions s
5754 in exts == ".hs" && not ("-out" `isSuffixOf` s')
5855
5956-- | For given path of input file return expected name of output.
6057deriveOutput :: Path Rel File -> IO (Path Rel File)
6158deriveOutput path =
6259 parseRelFile $
63− addExtension (dropExtensions (fromRelFile path) ++ "-out") "hs"
60+ F.addExtension (F.dropExtensions (fromRelFile path) ++ "-out") "hs"
6461
6562-- | A version of 'shouldBe' that is specialized to comparing 'Text' values.
6663-- It also prints multi-line snippets in a more readable form.
6764shouldMatch :: Bool -> Text -> Text -> Expectation
68−shouldMatch idempotencyTest actual expected =
65+shouldMatch idempotenceTest actual expected =
6966 when (actual /= expected) . expectationFailure $
7067 unlines
7168 [ ">>>>>>>>>>>>>>>>>>>>>> expected (" ++ pass ++ "):",
@@ -75,8 +72,8 @@ shouldMatch idempotencyTest actual expected =
7572 ]
7673 where
7774 pass =
78− if idempotencyTest
79− then "idempotency pass"
75+ if idempotenceTest
76+ then "idempotence pass"
8077 else "first pass"
8178
8279examplesDir :: Path Rel Dir