fourmolu 0.19.0.1 → 0.20.1.0
raw patch · 217 files changed
Files
- CHANGELOG.md +130/−30
- README.md +17/−6
- app/Main.hs +1/−2
- data/examples/declaration/data/linear-four-out.hs +11/−0
- data/examples/declaration/data/linear-out.hs +11/−0
- data/examples/declaration/data/linear.hs +10/−0
- data/examples/declaration/data/record-four-out.hs +1/−1
- data/examples/declaration/data/record-out.hs +1/−1
- data/examples/declaration/data/record.hs +1/−1
- data/examples/declaration/data/required-type-arguments-four-out.hs +25/−0
- data/examples/declaration/data/required-type-arguments-out.hs +27/−0
- data/examples/declaration/data/required-type-arguments.hs +24/−0
- data/examples/declaration/foreign/foreign-import-multiline-four-out.hs +8/−0
- data/examples/declaration/foreign/foreign-import-multiline-out.hs +8/−0
- data/examples/declaration/foreign/foreign-import-multiline.hs +6/−0
- data/examples/declaration/rewrite-rule/prelude2-four-out.hs +1/−1
- data/examples/declaration/rewrite-rule/prelude2-out.hs +1/−1
- data/examples/declaration/rewrite-rule/prelude2.hs +1/−1
- data/examples/declaration/signature/specialize/specialize-2-four-out.hs +8/−0
- data/examples/declaration/signature/specialize/specialize-2-out.hs +8/−0
- data/examples/declaration/signature/specialize/specialize-2.hs +9/−0
- data/examples/declaration/signature/specialize/specialize-3-four-out.hs +10/−0
- data/examples/declaration/signature/specialize/specialize-3-out.hs +10/−0
- data/examples/declaration/signature/specialize/specialize-3.hs +4/−0
- data/examples/declaration/value/function/arrow/proc-do-complex-four-out.hs +1/−1
- data/examples/declaration/value/function/arrow/proc-do-complex-out.hs +1/−1
- data/examples/declaration/value/function/arrow/proc-do-complex.hs +1/−1
- data/examples/declaration/value/function/case-single-line-with-braces-four-out.hs +2/−0
- data/examples/declaration/value/function/case-single-line-with-braces-out.hs +2/−0
- data/examples/declaration/value/function/case-single-line-with-braces.hs +2/−0
- data/examples/declaration/value/function/case-with-comment-before-where-four-out.hs +14/−0
- data/examples/declaration/value/function/case-with-comment-before-where-out.hs +14/−0
- data/examples/declaration/value/function/case-with-comment-before-where.hs +14/−0
- data/examples/declaration/value/function/do-multiline-with-case-four-out.hs +13/−0
- data/examples/declaration/value/function/do-multiline-with-case-out.hs +13/−0
- data/examples/declaration/value/function/do-multiline-with-case.hs +13/−0
- data/examples/declaration/value/function/do-single-line-case-guards-four-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-case-guards-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-case-guards.hs +2/−0
- data/examples/declaration/value/function/do-single-line-lambda-case-four-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-lambda-case-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-lambda-case.hs +2/−0
- data/examples/declaration/value/function/do-single-line-multiple-cases-four-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-multiple-cases-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-multiple-cases.hs +2/−0
- data/examples/declaration/value/function/do-single-line-nested-case-four-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-nested-case-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-nested-case.hs +2/−0
- data/examples/declaration/value/function/do-single-line-with-case-four-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-with-case-out.hs +2/−0
- data/examples/declaration/value/function/do-single-line-with-case.hs +2/−0
- data/examples/declaration/value/function/guards-four-out.hs +2/−0
- data/examples/declaration/value/function/guards-out.hs +2/−0
- data/examples/declaration/value/function/guards.hs +2/−0
- data/examples/declaration/value/function/infix/dollar-chains-1-four-out.hs +3/−3
- data/examples/declaration/value/function/infix/dollar-chains-1-out.hs +3/−3
- data/examples/declaration/value/function/infix/dollar-chains-3-four-out.hs +8/−8
- data/examples/declaration/value/function/infix/dollar-chains-3-out.hs +8/−8
- data/examples/declaration/value/function/infix/dollar-chains-4-four-out.hs +4/−4
- data/examples/declaration/value/function/infix/dollar-chains-4-out.hs +4/−4
- data/examples/declaration/value/function/infix/lenses-four-out.hs +8/−8
- data/examples/declaration/value/function/infix/lenses-out.hs +8/−8
- data/examples/declaration/value/function/infix/op-chain-r-eq-dollar-1-four-out.hs +8/−8
- data/examples/declaration/value/function/infix/op-chain-r-eq-dollar-1-out.hs +8/−8
- data/examples/declaration/value/function/infix/op-chain-r-eq-dollar-2-four-out.hs +8/−8
- data/examples/declaration/value/function/infix/op-chain-r-eq-dollar-2-out.hs +8/−8
- data/examples/declaration/value/function/infix/op-chain-r-s-dollar-four-out.hs +4/−4
- data/examples/declaration/value/function/infix/op-chain-r-s-dollar-out.hs +4/−4
- data/examples/declaration/value/function/infix/qualified-ops-four-out.hs +8/−8
- data/examples/declaration/value/function/infix/qualified-ops-out.hs +8/−8
- data/examples/declaration/value/function/linear-bindings-four-out.hs +9/−0
- data/examples/declaration/value/function/linear-bindings-out.hs +9/−0
- data/examples/declaration/value/function/linear-bindings.hs +9/−0
- data/examples/declaration/value/function/multiline-strings-9-four-out.hs +11/−0
- data/examples/declaration/value/function/multiline-strings-9-out.hs +11/−0
- data/examples/declaration/value/function/multiline-strings-9.hs +11/−0
- data/examples/declaration/value/function/pattern/or-patterns-four-out.hs +9/−1
- data/examples/declaration/value/function/pattern/or-patterns-out.hs +9/−1
- data/examples/declaration/value/function/pattern/or-patterns.hs +7/−1
- data/examples/declaration/value/function/strings-four-out.hs +2/−0
- data/examples/declaration/value/function/strings-out.hs +2/−0
- data/examples/declaration/value/function/strings.hs +2/−0
- data/examples/declaration/value/pattern-synonyms/explicitely-bidirectional-four-out.hs +8/−8
- data/examples/fixity/megaparsec-alternative-four-out.hs +8/−0
- data/examples/fixity/megaparsec-alternative-out.hs +8/−0
- data/examples/fixity/megaparsec-alternative.hs +8/−0
- data/examples/fixity/optics-mixed-four-out.hs +8/−0
- data/examples/fixity/optics-mixed-out.hs +8/−0
- data/examples/fixity/optics-mixed.hs +8/−0
- data/examples/fixity/relude-default-operator-chain-four-out.hs +10/−0
- data/examples/fixity/relude-default-operator-chain-out.hs +10/−0
- data/examples/fixity/relude-default-operator-chain.hs +10/−0
- data/examples/fixity/relude-default-operator-four-out.hs +7/−0
- data/examples/fixity/relude-default-operator-out.hs +7/−0
- data/examples/fixity/relude-default-operator.hs +7/−0
- data/examples/fixity/rio-ampersand-four-out.hs +7/−0
- data/examples/fixity/rio-ampersand-out.hs +7/−0
- data/examples/fixity/rio-ampersand.hs +7/−0
- data/examples/fixity/rio-deepseq-four-out.hs +7/−0
- data/examples/fixity/rio-deepseq-out.hs +7/−0
- data/examples/fixity/rio-deepseq.hs +7/−0
- data/examples/import/data-four-out.hs +3/−0
- data/examples/import/data-out.hs +3/−0
- data/examples/import/data.hs +6/−0
- data/examples/import/explicit-level-imports-four-out.hs +13/−0
- data/examples/import/explicit-level-imports-out.hs +13/−0
- data/examples/import/explicit-level-imports-qualified-post-four-out.hs +5/−0
- data/examples/import/explicit-level-imports-qualified-post-out.hs +5/−0
- data/examples/import/explicit-level-imports-qualified-post.hs +5/−0
- data/examples/import/explicit-level-imports.hs +14/−0
- data/examples/import/implicit-prelude-package-four-out.hs +7/−0
- data/examples/import/implicit-prelude-package-out.hs +7/−0
- data/examples/import/implicit-prelude-package.hs +7/−0
- data/examples/import/no-implicit-prelude-four-out.hs +6/−0
- data/examples/import/no-implicit-prelude-out.hs +6/−0
- data/examples/import/no-implicit-prelude-package-four-out.hs +8/−0
- data/examples/import/no-implicit-prelude-package-out.hs +8/−0
- data/examples/import/no-implicit-prelude-package.hs +8/−0
- data/examples/import/no-implicit-prelude.hs +6/−0
- data/examples/module-header/block-haddock-in-export-list-four-out.hs +4/−0
- data/examples/module-header/block-haddock-in-export-list-out.hs +5/−0
- data/examples/module-header/block-haddock-in-export-list.hs +1/−0
- data/examples/other/comment-trailing-blank-line-do-four-out.hs +4/−0
- data/examples/other/comment-trailing-blank-line-do-out.hs +4/−0
- data/examples/other/comment-trailing-blank-line-do.hs +4/−0
- data/examples/other/comment-trailing-blank-line-let-four-out.hs +5/−0
- data/examples/other/comment-trailing-blank-line-let-out.hs +5/−0
- data/examples/other/comment-trailing-blank-line-let.hs +5/−0
- data/examples/other/comment-trailing-blank-line-variants-four-out.hs +22/−0
- data/examples/other/comment-trailing-blank-line-variants-out.hs +22/−0
- data/examples/other/comment-trailing-blank-line-variants.hs +22/−0
- data/examples/other/comment-trailing-blank-line-where-four-out.hs +5/−0
- data/examples/other/comment-trailing-blank-line-where-out.hs +5/−0
- data/examples/other/comment-trailing-blank-line-where.hs +5/−0
- data/examples/other/comment-trailing-no-blank-line-four-out.hs +25/−0
- data/examples/other/comment-trailing-no-blank-line-out.hs +25/−0
- data/examples/other/comment-trailing-no-blank-line.hs +25/−0
- data/examples/other/empty-forall-four-out.hs +1/−1
- data/examples/other/empty-forall-out.hs +1/−1
- data/fourmolu/haddock-style/output-multi_line-module=multi_line.hs +1/−1
- data/fourmolu/haddock-style/output-multi_line-module=multi_line_compact.hs +1/−1
- data/fourmolu/haddock-style/output-multi_line-module=single_line.hs +1/−1
- data/fourmolu/haddock-style/output-multi_line.hs +1/−1
- data/fourmolu/haddock-style/output-multi_line_compact-module=multi_line.hs +1/−1
- data/fourmolu/haddock-style/output-multi_line_compact-module=multi_line_compact.hs +1/−1
- data/fourmolu/haddock-style/output-multi_line_compact-module=single_line.hs +1/−1
- data/fourmolu/haddock-style/output-multi_line_compact.hs +1/−1
- data/fourmolu/import-grouping/input.hs +3/−0
- data/fourmolu/import-grouping/output-by_qualified.hs +3/−0
- data/fourmolu/import-grouping/output-by_scope.hs +3/−0
- data/fourmolu/import-grouping/output-by_scope_then_qualified.hs +3/−0
- data/fourmolu/import-grouping/output-custom.hs +4/−0
- data/fourmolu/import-grouping/output-preserve.hs +3/−0
- data/fourmolu/import-grouping/output-single.hs +3/−0
- data/fourmolu/indentation/input.hs +6/−0
- data/fourmolu/indentation/output-2-indent_wheres.hs +6/−0
- data/fourmolu/indentation/output-2.hs +6/−0
- data/fourmolu/indentation/output-3-indent_wheres.hs +6/−0
- data/fourmolu/indentation/output-3.hs +6/−0
- data/fourmolu/indentation/output-4-indent_wheres.hs +6/−0
- data/fourmolu/indentation/output-4.hs +6/−0
- data/fourmolu/record-style/input.hs +24/−0
- data/fourmolu/record-style/output-aligned-leading.hs +35/−0
- data/fourmolu/record-style/output-aligned-trailing.hs +35/−0
- data/fourmolu/record-style/output-knr-leading.hs +35/−0
- data/fourmolu/record-style/output-knr-trailing.hs +35/−0
- data/fourmolu/trailing-section-operators/output-True.hs +5/−5
- data/fourmolu/unicode-syntax/output-always.hs +2/−2
- data/fourmolu/unicode-syntax/output-detect.hs +2/−2
- data/fourmolu/unicode-syntax/output-never.hs +2/−2
- extract-hackage-info/hackage-info.bin binary
- fixity-tests/test-1-no-extra-info-expected.hs +11/−11
- fixity-tests/test-1-with-fixity-info-expected.hs +8/−8
- fourmolu.cabal +15/−14
- fourmolu.yaml +1/−0
- src/GHC/DynFlags.hs +6/−0
- src/Ormolu.hs +3/−2
- src/Ormolu/Config.hs +3/−1
- src/Ormolu/Config/Gen.hs +41/−0
- src/Ormolu/Config/Types.hs +47/−40
- src/Ormolu/Diff/ParseResult.hs +18/−4
- src/Ormolu/Diff/Text.hs +0/−4
- src/Ormolu/Fixity/Internal.hs +2/−56
- src/Ormolu/Imports.hs +47/−14
- src/Ormolu/Imports/Grouping.hs +35/−10
- src/Ormolu/Parser.hs +27/−8
- src/Ormolu/Printer.hs +3/−1
- src/Ormolu/Printer/Combinators.hs +52/−2
- src/Ormolu/Printer/Internal.hs +18/−4
- src/Ormolu/Printer/Meat/Common.hs +26/−17
- src/Ormolu/Printer/Meat/Declaration.hs +7/−1
- src/Ormolu/Printer/Meat/Declaration/Class.hs +7/−2
- src/Ormolu/Printer/Meat/Declaration/Data.hs +131/−58
- src/Ormolu/Printer/Meat/Declaration/Foreign.hs +7/−2
- src/Ormolu/Printer/Meat/Declaration/OpTree.hs +21/−8
- src/Ormolu/Printer/Meat/Declaration/Rule.hs +15/−13
- src/Ormolu/Printer/Meat/Declaration/Signature.hs +67/−13
- src/Ormolu/Printer/Meat/Declaration/Signature.hs-boot +14/−0
- src/Ormolu/Printer/Meat/Declaration/StringLiteral.hs +18/−5
- src/Ormolu/Printer/Meat/Declaration/Type.hs +9/−2
- src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs +9/−4
- src/Ormolu/Printer/Meat/Declaration/Value.hs +146/−106
- src/Ormolu/Printer/Meat/ImportExport.hs +34/−6
- src/Ormolu/Printer/Meat/Module.hs +32/−18
- src/Ormolu/Printer/Meat/Type.hs +186/−359
- src/Ormolu/Printer/Meat/Type/Function.hs +493/−0
- src/Ormolu/Printer/Operators.hs +3/−3
- src/Ormolu/Processing/Preprocess.hs +12/−5
- src/Ormolu/Utils.hs +3/−7
- src/Ormolu/Utils/Glob.hs +4/−0
- src/Ormolu/Utils/IO.hs +30/−19
- tests/Ormolu/Config/PrinterOptsSpec.hs +80/−61
- tests/Ormolu/ConfigSpec.hs +92/−199
- tests/Ormolu/Fixity/ParserSpec.hs +1/−1
- tests/Ormolu/FixitySpec.hs +7/−19
- tests/Ormolu/Integration/Utils.hs +3/−2
- tests/Ormolu/PrinterSpec.hs +4/−0
CHANGELOG.md view
@@ -1,6 +1,106 @@+## Fourmolu 0.20.1.0++### Upstream changes:++#### Ormolu 0.8.2.0++* Overhaul how operator fixity information is collected. In addition to the+ Hoogle database, Ormolu now parses the sources of a curated set of important+ packages directly, which yields more accurate and complete fixity data than+ before. In particular it recovers fixities for operators re-exported through+ umbrella modules (e.g. `Servant.API`, `Control.Lens`) that recent Hoogle+ databases no longer record. As a result, formatting of operator chains is+ improved out of the box, and users should expect some operator-heavy code to+ be laid out differently (and more correctly) than in previous releases.++* Improve the layout of chains of `infixr 0` operators (`$`, `seq`, `?:`, and+ the like). Such operators are only laid out in the trailing "staircase" style+ when it is warranted: either the chain consists of a single operator, or its+ final operand is a hanging construct (a `do` block, lambda, `case`, etc.). A+ chain of several such operators that ends in an ordinary expression is now+ laid out with the operators in the leading position instead of an+ ever-deepening pyramid. [Issue+ 1151](https://github.com/tweag/ormolu/issues/1151).++* Do not crash when a parent directory cannot be read due to insufficient+ permissions while searching for configuration files; the search for+ configuration files is stopped at that point instead. [Issue+ 1212](https://github.com/tweag/ormolu/issues/1212).++* Preserve blank lines between blocks in layout contexts (`where`, `do`,+ `let`) when the preceding block ends with a trailing comment. [Issue+ 1132](https://github.com/tweag/ormolu/issues/1132).++* Fix printing of single line export lists with inlined Haddock comments.+ [Issue 1051](https://github.com/tweag/ormolu/issues/1051).++* Fix preservation of the position of comments around the `where` keyword.+ [Issue 784](https://github.com/tweag/ormolu/issues/784).++* Do not sort `Prelude` to the end of the import list when the+ `NoImplicitPrelude` extension is enabled; instead sort it like any other+ import. [Issue 1189](https://github.com/tweag/ormolu/issues/1189).++## Fourmolu 0.20.0.0++Now installable with GHCup! See README for details.++* Improve `import-grouping` configuration++ * Add the `scope` option for import grouping ([#525](https://github.com/fourmolu/fourmolu/pull/525)).++ * Deprecate the `match` option for import grouping in favor of `glob` and `scope` ([#525](https://github.com/fourmolu/fourmolu/pull/525)).++ * Add the `import-list` option for import grouping ([#522](https://github.com/fourmolu/fourmolu/pull/522)).++* Add a `record-style` to configure the placement of record braces. ([#467](https://github.com/fourmolu/fourmolu/issues/467))++* Fix applying `indent-wheres` configuration to pattern synonyms with explicit bidirectional `where` clauses ([#486](https://github.com/fourmolu/fourmolu/issues/486))++### Upstream changes:++#### Ormolu 0.8.1.1++* Add missing braces for case expressions in single‑line do blocks. [Issue 1180](https://github.com/tweag/ormolu/issues/1180).++* Fix the import grouping logic in the presence of imports with explicit levels. [Issue 1192](https://github.com/tweag/ormolu/issues/1192).++#### Ormolu 0.8.1.0++* Fix printing of guards on pattern binds. [Issue+ 1178](https://github.com/tweag/ormolu/issues/1178).++* Switched to `ghc-lib-parser-9.14`, with the following new syntactic features:+ * GHC proposal [#493](https://github.com/ghc-proposals/ghc-proposals/blob/e2c683698323cec3e33625369ae2b5f585387c70/proposals/0493-specialise-expressions.rst): expressions in SPECIALISE pragmas+ * Multiline strings in foreign import declarations.+ * `ExplicitNamespaces` supports the `data` namespace specifier in import and export lists, replacing `pattern`.+ * `LinearTypes` adds new syntax to support non-linear record fields.+ * `RequiredTypeArguments` allows visible forall in GADT syntax.++* Updated to `Cabal-syntax-3.16`.++* Correctly format string literals containing the `\^\` escape sequence. [Issue+ 1165](https://github.com/tweag/ormolu/issues/1165).++* Correctly preserve consecutive blank lines in multiline strings. [Issue+ 1194](https://github.com/tweag/ormolu/issues/1194).++* Fix printing of multi-line or-patterns inside as-patterns. [Issue+ 1183](https://github.com/tweag/ormolu/issues/1183).++* Fix an issue where or-patterns would be indented twice. [Issue+ 1188](https://github.com/tweag/ormolu/issues/1188).++* Add support for `ExplicitLevelImports`. [Issue+ 1192](https://github.com/tweag/ormolu/issues/1192).+ ## Fourmolu 0.19.0.1 -* Fixed a performance regression introduced in 0.8.0.0. [Issue+### Upstream changes:++#### Ormolu 0.8.0.2++* Fix a performance regression introduced in 0.8.0.0. [Issue 1176](https://github.com/tweag/ormolu/issues/1176). ## Fourmolu 0.19.0.0@@ -429,7 +529,7 @@ #### Ormolu 0.5.0.1 -* Fixed a bug in the diff printing functionality. [Issue+* Fix a bug in the diff printing functionality. [Issue 886](https://github.com/tweag/ormolu/issues/886). * Indent closing bracket for list comprehensions in `do` blocks.@@ -722,7 +822,7 @@ * Now `--mode check` fails on missing trailing blank lines. [Issue 743](https://github.com/tweag/ormolu/issues/743). -* Fixed indentation of arrow forms in do blocks. [Issue+* Fix indentation of arrow forms in do blocks. [Issue 739](https://github.com/tweag/ormolu/issues/739). #### Ormolu 0.1.4.1@@ -735,7 +835,7 @@ * Added support for monad comprehensions. [Issue 665](https://github.com/tweag/ormolu/issues/665). -* Fixed a bug when a space was inserted in front of promoted types even when+* Fix a bug when a space was inserted in front of promoted types even when it wasn't strictly necessary. [Issue 668](https://github.com/tweag/ormolu/issues/668). @@ -745,7 +845,7 @@ #### Ormolu 0.1.3.1 -* Fixed a problem with multiline record updates using the record dot+* Fix a problem with multiline record updates using the record dot preprocessor. [Issue 658](https://github.com/tweag/ormolu/issues/658). ## Fourmolu 0.3.0.0@@ -780,7 +880,7 @@ #### Ormolu 0.1.2.0: -* Fixed the bug when comments in different styles got glued together after+* Fix the bug when comments in different styles got glued together after formatting. [Issue 589](https://github.com/tweag/ormolu/issues/589). * Added `-i` as a shortcut for `--mode inplace`. [Issue@@ -809,26 +909,26 @@ * Improved sorting of operators in imports. [Issue 602](https://github.com/tweag/ormolu/issues/602). -* Fixed a bug related to trailing space in multiline comments in certain+* Fix a bug related to trailing space in multiline comments in certain cases. [Issue 603](https://github.com/tweag/ormolu/issues/602). * Added support for formatting linked lists with `(:)` as line terminator. [Issue 478](https://github.com/tweag/ormolu/issues/478). -* Fixed rendering of function arguments in multiline layout. [Issue+* Fix rendering of function arguments in multiline layout. [Issue 609](https://github.com/tweag/ormolu/issues/609). * Blank lines between definitions in `let` and `while` bindings are now preserved. [Issue 554](https://github.com/tweag/ormolu/issues/554). -* Fixed the bug when type applications stuck to the `$` of TH splices that+* Fix the bug when type applications stuck to the `$` of TH splices that followed them. [Issue 613](https://github.com/tweag/ormolu/issues/613). * Improved region formatting so that indented fragments—such as definitions inside of `where` clauses—can be formatted. [Issue 572](https://github.com/tweag/ormolu/issues/572). -* Fixed the bug related to the de-association of pragma comments. [Issue+* Fix the bug related to the de-association of pragma comments. [Issue 619](https://github.com/tweag/ormolu/issues/619). ## Fourmolu 0.1.0.0@@ -841,24 +941,24 @@ #### Ormolu 0.1.0.0: -* Fixed rendering of type signatures concerning several identifiers. [Issue+* Fix rendering of type signatures concerning several identifiers. [Issue 566](https://github.com/tweag/ormolu/issues/566). -* Fixed an idempotence issue with inline comments in tuples and parentheses.+* Fix an idempotence issue with inline comments in tuples and parentheses. [Issue 450](https://github.com/tweag/ormolu/issues/450). -* Fixed an idempotence issue when certain comments were picked up as+* Fix an idempotence issue when certain comments were picked up as “continuation” of a series of comments [Issue 449](https://github.com/tweag/ormolu/issues/449). -* Fixed an idempotence issue related to different indentation levels in a+* Fix an idempotence issue related to different indentation levels in a comment series. [Issue 512](https://github.com/tweag/ormolu/issues/512). -* Fixed an idempotence issue related to comments which may happen to be+* Fix an idempotence issue related to comments which may happen to be separated from the elements they are attached to by the equality sign. [Issue 340](https://github.com/tweag/ormolu/issues/340). -* Fixed an idempotence issue with type synonym and data declarations where+* Fix an idempotence issue with type synonym and data declarations where the type has a Haddock. [Issue 578](https://github.com/tweag/ormolu/issues/578). @@ -866,17 +966,17 @@ multiple blank lines in a row. [Issue 518](https://github.com/tweag/ormolu/issues/518). -* Fixed rendering of comments around if expressions. [Issue+* Fix rendering of comments around if expressions. [Issue 458](https://github.com/tweag/ormolu/issues/458). * Unnamed fields of data constructors are now documented using the `-- ^` syntax. [Issue 445](https://github.com/tweag/ormolu/issues/445) and [Issue 428](https://github.com/tweag/ormolu/issues/428). -* Fixed non-idempotent transformation of partly documented data definition.+* Fix non-idempotent transformation of partly documented data definition. [Issue 590](https://github.com/tweag/ormolu/issues/590). -* Fixed an idempotence issue related to operators. [Issue+* Fix an idempotence issue related to operators. [Issue 522](https://github.com/tweag/ormolu/issues/522). * Renamed the `--check-idempotency` flag to `--check-idempotence`.@@ -907,7 +1007,7 @@ select a region to format. [Issue 516](https://github.com/tweag/ormolu/issues/516). -* Fixed rendering of module headers in the presence of preceding comments or+* Fix rendering of module headers in the presence of preceding comments or Haddocks. [Issue 561](https://github.com/tweag/ormolu/issues/561). ## Ormolu 0.0.4.0@@ -928,7 +1028,7 @@ now put on its own line. [Issue 509](https://github.com/tweag/ormolu/issues/509). -* Fixed the bug pertaining to rendering of arrow notation with multiline+* Fix the bug pertaining to rendering of arrow notation with multiline expressions. [Issue 513](https://github.com/tweag/ormolu/issues/513). * Made rendering of data type definitions, value-level applications, and@@ -946,15 +1046,15 @@ ## Ormolu 0.0.3.1 -* Fixed rendering of record updates with the record dot preprocessor syntax+* Fix rendering of record updates with the record dot preprocessor syntax [Issue 498](https://github.com/tweag/ormolu/issues/498). ## Ormolu 0.0.3.0 -* Fixed an issue related to unnecessary use of curly braces. [Issue+* Fix an issue related to unnecessary use of curly braces. [Issue 473](https://github.com/tweag/ormolu/issues/473). -* Fixed the issue with formatting multi-way if when it happens to be a+* Fix the issue with formatting multi-way if when it happens to be a function applied to arguments [Issue 488](https://github.com/tweag/ormolu/issues/488). This changed the way multi-line if is formatted in general.@@ -966,7 +1066,7 @@ potentially-hanging consturctions in the presence of comments. [Issue 447](https://github.com/tweag/ormolu/issues/447). -* Fixed indentation in presence of type applications. [Issue+* Fix indentation in presence of type applications. [Issue 493](https://github.com/tweag/ormolu/issues/493). * Class and instance declarations now do not have a blank line after@@ -984,20 +1084,20 @@ * Now unrecognized GHC options passed with `--ghc-opt` cause Ormolu to fail (exit code 7). -* Fixed formatting of result type in closed type families. See [issue+* Fix formatting of result type in closed type families. See [issue 420](https://github.com/tweag/ormolu/issues/420). -* Fixed a minor inconsistency between formatting of normal and foreign type+* Fix a minor inconsistency between formatting of normal and foreign type signatures. See [issue 408](https://github.com/tweag/ormolu/issues/408). -* Fixed a bug when comment before module header with Haddock was moved+* Fix a bug when comment before module header with Haddock was moved inside the export list. See [issue 430](https://github.com/tweag/ormolu/issues/430). * Empty `forall`s are now correctly preserved. See [issue 429](https://github.com/tweag/ormolu/issues/429). -* Fixed [issue 446](https://github.com/tweag/ormolu/issues/446), which+* Fix [issue 446](https://github.com/tweag/ormolu/issues/446), which involved braces and operators. * When there are comments between preceding Haddock (pipe-style) and its@@ -1017,7 +1117,7 @@ * Sorting language pragmas cannot not change meaning of the input program anymore. [Issue 404](https://github.com/tweag/ormolu/issues/404). -* Fixed formatting of applications where function is a complex expression.+* Fix formatting of applications where function is a complex expression. [Issue 444](https://github.com/tweag/ormolu/issues/444). ## Ormolu 0.0.1.0
README.md view
@@ -22,7 +22,7 @@ * [Contributing](#contributing) * [License](#license) -Fourmolu is a formatter for Haskell source code. It is a fork of [Ormolu](https://github.com/tweag/ormolu), with upstream improvements continually merged.+Fourmolu is a formatter for Haskell source code. It is a fork of [Ormolu](https://github.com/mrkkrp/ormolu), with upstream improvements continually merged. We share all bar one of Ormolu's goals: @@ -49,13 +49,24 @@ ## Installation -To install the latest release from Hackage, simply install with Cabal or Stack:+### (Recommended) Install with GHCup -```console-$ cabal install fourmolu-$ stack install fourmolu+```bash+ghcup config add-release-channel 3rdparty+ghcup install fourmolu latest ``` +### Install with Cabal/Stack++```bash+cabal install fourmolu+stack install fourmolu+```++### Install with [dotslash](https://dotslash-cli.com/docs/)++Copy the configuration in the GitHub release notes.+ ## Building from source ```console@@ -271,7 +282,7 @@ * CPP support is experimental. CPP is virtually impossible to handle correctly, so we process them as a sort of unchangeable snippets. This works only in simple cases when CPP conditionals surround top-level- declarations. See the [CPP](https://github.com/tweag/ormolu/blob/master/DESIGN.md#cpp) section in the design notes for a+ declarations. See the [CPP](https://github.com/mrkkrp/ormolu/blob/master/DESIGN.md#cpp) section in the design notes for a discussion of the dangers. * Various minor idempotence issues, most of them are related to comments or column limits.
app/Main.hs view
@@ -468,7 +468,7 @@ help "Fail if formatting is not idempotent" ] -- We cannot parse the source type here, because we might need to do- -- autodection based on the input file extension (not available here)+ -- autodetection based on the input file extension (not available here) -- before storing the resolved value in the config struct. <*> pure ModuleSource <*> (option parseColorMode . mconcat)@@ -492,7 +492,6 @@ <*> pure defaultPrinterOpts -- unused; overwritten in resolveConfig <*> (fmap Set.fromList . many . strOption . mconcat) [ long "local-modules",- short 'm', metavar "LOCAL_MODULES", help "Modules Fourmolu should consider as local by the current Cabal package" ]
+ data/examples/declaration/data/linear-four-out.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE LinearTypes #-}++data Record = Rec {x %'Many :: Int, y :: Char}++data T2 a b c where+ MkT2 :: a -> b %1 -> c %1 -> T2 a b c++data T2 a b c = MkT2 {x %Many :: a, y :: b, z :: c}++data T3 a m where+ MkT3 :: a %m -> T3 a m
+ data/examples/declaration/data/linear-out.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE LinearTypes #-}++data Record = Rec {x %'Many :: Int, y :: Char}++data T2 a b c where+ MkT2 :: a -> b %1 -> c %1 -> T2 a b c++data T2 a b c = MkT2 {x %Many :: a, y :: b, z :: c}++data T3 a m where+ MkT3 :: a %m -> T3 a m
+ data/examples/declaration/data/linear.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE LinearTypes #-}+data Record = Rec { x %'Many :: Int, y :: Char }++data T2 a b c where+ MkT2 :: a -> b %1 -> c %1 -> T2 a b c++data T2 a b c = MkT2 { x %Many :: a, y :: b, z :: c }++data T3 a m where+ MkT3 :: a %m -> T3 a m
data/examples/declaration/data/record-four-out.hs view
@@ -11,7 +11,7 @@ , fooGag , fooGog :: NonEmpty- ( Indentity+ ( Identity Bool ) -- ^ GagGog
data/examples/declaration/data/record-out.hs view
@@ -12,7 +12,7 @@ fooGag, fooGog :: NonEmpty- ( Indentity+ ( Identity Bool ), -- | Huh!
data/examples/declaration/data/record.hs view
@@ -6,7 +6,7 @@ { fooX :: Int -- ^ X , fooY :: Int -- ^ Y , fooBar, fooBaz :: NonEmpty (Identity Bool) -- ^ BarBaz- , fooGag, fooGog :: NonEmpty (Indentity+ , fooGag, fooGog :: NonEmpty (Identity Bool) -- ^ GagGog , fooFoo
+ data/examples/declaration/data/required-type-arguments-four-out.hs view
@@ -0,0 +1,25 @@+data T a where+ Typed :: forall a -> a -> T a++f1 (Typed a x) = x :: a+f2 (Typed Int n) = n * 2+f3 (Typed ((->) w Bool) g) = not . g++data D x where+ MkD1 ::+ forall a b ->+ a ->+ b ->+ D (a, b)+ MkD2 ::+ forall a.+ forall b ->+ a ->+ b ->+ D (a, b)+ MkD3 ::+ forall a ->+ a ->+ forall b ->+ b ->+ D (a, b)
+ data/examples/declaration/data/required-type-arguments-out.hs view
@@ -0,0 +1,27 @@+data T a where+ Typed :: forall a -> a -> T a++f1 (Typed a x) = x :: a++f2 (Typed Int n) = n * 2++f3 (Typed ((->) w Bool) g) = not . g++data D x where+ MkD1 ::+ forall a b ->+ a ->+ b ->+ D (a, b)+ MkD2 ::+ forall a.+ forall b ->+ a ->+ b ->+ D (a, b)+ MkD3 ::+ forall a ->+ a ->+ forall b ->+ b ->+ D (a, b)
+ data/examples/declaration/data/required-type-arguments.hs view
@@ -0,0 +1,24 @@+data T a where+ Typed :: forall a -> a -> T a++f1 (Typed a x) = x :: a+f2 (Typed Int n) = n*2+f3 (Typed ((->) w Bool) g) = not . g++data D x where+ MkD1 :: forall a b ->+ a ->+ b ->+ D (a, b)++ MkD2 :: forall a.+ forall b ->+ a ->+ b ->+ D (a, b)++ MkD3 :: forall a ->+ a ->+ forall b ->+ b ->+ D (a, b)
+ data/examples/declaration/foreign/foreign-import-multiline-four-out.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE MultilineStrings #-}++foreign import capi+ """+ foo+ bar+ """+ foo :: Int -> Int
+ data/examples/declaration/foreign/foreign-import-multiline-out.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE MultilineStrings #-}++foreign import capi+ """+ foo+ bar+ """+ foo :: Int -> Int
+ data/examples/declaration/foreign/foreign-import-multiline.hs view
@@ -0,0 +1,6 @@+{-# language MultilineStrings #-}++foreign import capi """+ foo+ bar+ """ foo :: Int -> Int
data/examples/declaration/rewrite-rule/prelude2-four-out.hs view
@@ -11,7 +11,7 @@ -- when we disable the rule that expands (++) into foldr -- The foldr/cons rule looks nice, but it can give disastrously--- bloated code when commpiling+-- bloated code when compiling -- array (a,b) [(1,2), (2,2), (3,2), ...very long list... ] -- i.e. when there are very very long literal lists -- So I've disabled it for now. We could have special cases
data/examples/declaration/rewrite-rule/prelude2-out.hs view
@@ -11,7 +11,7 @@ -- when we disable the rule that expands (++) into foldr -- The foldr/cons rule looks nice, but it can give disastrously--- bloated code when commpiling+-- bloated code when compiling -- array (a,b) [(1,2), (2,2), (3,2), ...very long list... ] -- i.e. when there are very very long literal lists -- So I've disabled it for now. We could have special cases
data/examples/declaration/rewrite-rule/prelude2.hs view
@@ -11,7 +11,7 @@ -- when we disable the rule that expands (++) into foldr -- The foldr/cons rule looks nice, but it can give disastrously--- bloated code when commpiling+-- bloated code when compiling -- array (a,b) [(1,2), (2,2), (3,2), ...very long list... ] -- i.e. when there are very very long literal lists -- So I've disabled it for now. We could have special cases
+ data/examples/declaration/signature/specialize/specialize-2-four-out.hs view
@@ -0,0 +1,8 @@+{-# SPECIALIZE addMult @Double #-}+{-# SPECIALIZE addMult (5 :: Int) #-}+{-# SPECIALIZE addMult 5 :: Int -> Int #-}++{-# SPECIALIZE [1] forall x y. f @Int True (x, y) #-}++{-# SPECIALIZE forall x xs. loop (x : xs)+ #-}
+ data/examples/declaration/signature/specialize/specialize-2-out.hs view
@@ -0,0 +1,8 @@+{-# SPECIALIZE addMult @Double #-}+{-# SPECIALIZE addMult (5 :: Int) #-}+{-# SPECIALIZE addMult 5 :: Int -> Int #-}++{-# SPECIALIZE [1] forall x y. f @Int True (x, y) #-}++{-# SPECIALIZE forall x xs. loop (x : xs)+ #-}
+ data/examples/declaration/signature/specialize/specialize-2.hs view
@@ -0,0 +1,9 @@+{-# SPECIALISE addMult @Double #-}+{-# SPECIALISE addMult (5 :: Int) #-}+{-# SPECIALISE addMult 5 :: Int -> Int #-}++{-# SPECIALISE [1] forall x y. f @Int True (x,y) #-}++{-# SPECIALISE+ forall x xs .+ loop (x:xs) #-}
+ data/examples/declaration/signature/specialize/specialize-3-four-out.hs view
@@ -0,0 +1,10 @@+sep, fsep, hsep :: (Applicative m, Foldable t) => t (m Doc) -> m Doc+sep = fmap P.sep . sequenceAFoldable+{-# SPECIALIZE NOINLINE sep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE sep :: List1 (TCM Doc) -> TCM Doc #-}+fsep = fmap P.fsep . sequenceAFoldable+{-# SPECIALIZE NOINLINE [2] fsep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE [2] fsep :: List1 (TCM Doc) -> TCM Doc #-}+hsep = fmap P.hsep . sequenceAFoldable+{-# SPECIALIZE NOINLINE [~2] hsep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE [~2] hsep :: List1 (TCM Doc) -> TCM Doc #-}
+ data/examples/declaration/signature/specialize/specialize-3-out.hs view
@@ -0,0 +1,10 @@+sep, fsep, hsep :: (Applicative m, Foldable t) => t (m Doc) -> m Doc+sep = fmap P.sep . sequenceAFoldable+{-# SPECIALIZE NOINLINE sep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE sep :: List1 (TCM Doc) -> TCM Doc #-}+fsep = fmap P.fsep . sequenceAFoldable+{-# SPECIALIZE NOINLINE [2] fsep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE [2] fsep :: List1 (TCM Doc) -> TCM Doc #-}+hsep = fmap P.hsep . sequenceAFoldable+{-# SPECIALIZE NOINLINE [~2] hsep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE [~2] hsep :: List1 (TCM Doc) -> TCM Doc #-}
+ data/examples/declaration/signature/specialize/specialize-3.hs view
@@ -0,0 +1,4 @@+sep, fsep, hsep :: (Applicative m, Foldable t) => t (m Doc) -> m Doc+sep = fmap P.sep . sequenceAFoldable ; {-# SPECIALIZE NOINLINE sep :: [TCM Doc] -> TCM Doc #-} ; {-# SPECIALIZE NOINLINE sep :: List1 (TCM Doc) -> TCM Doc #-}+fsep = fmap P.fsep . sequenceAFoldable ; {-# SPECIALIZE NOINLINE [2] fsep :: [TCM Doc] -> TCM Doc #-} ; {-# SPECIALIZE NOINLINE [2] fsep :: List1 (TCM Doc) -> TCM Doc #-}+hsep = fmap P.hsep . sequenceAFoldable ; {-# SPECIALIZE NOINLINE [~2] hsep :: [TCM Doc] -> TCM Doc #-} ; {-# SPECIALIZE NOINLINE [~2] hsep :: List1 (TCM Doc) -> TCM Doc #-}
data/examples/declaration/value/function/arrow/proc-do-complex-four-out.hs view
@@ -12,7 +12,7 @@ ) -> do -- Begin do- (x, y) <- -- GHC parser fails if layed out over multiple lines+ (x, y) <- -- GHC parser fails if laid out over multiple lines f -- Call into f ( a , c -- Tuple together arguments
data/examples/declaration/value/function/arrow/proc-do-complex-out.hs view
@@ -12,7 +12,7 @@ ) -> do -- Begin do- (x, y) <- -- GHC parser fails if layed out over multiple lines+ (x, y) <- -- GHC parser fails if laid out over multiple lines f -- Call into f ( a, c -- Tuple together arguments
data/examples/declaration/value/function/arrow/proc-do-complex.hs view
@@ -9,7 +9,7 @@ (e, f) ) -> do -- Begin do- (x,y) -- GHC parser fails if layed out over multiple lines+ (x,y) -- GHC parser fails if laid out over multiple lines <- f -- Call into f (a, c) -- Tuple together arguments
+ data/examples/declaration/value/function/case-single-line-with-braces-four-out.hs view
@@ -0,0 +1,2 @@+getValue :: Maybe Int -> Int+getValue x = case x of Just n -> n; Nothing -> 0
+ data/examples/declaration/value/function/case-single-line-with-braces-out.hs view
@@ -0,0 +1,2 @@+getValue :: Maybe Int -> Int+getValue x = case x of Just n -> n; Nothing -> 0
+ data/examples/declaration/value/function/case-single-line-with-braces.hs view
@@ -0,0 +1,2 @@+getValue :: Maybe Int -> Int+getValue x = case x of {Just n -> n; Nothing -> 0}
+ data/examples/declaration/value/function/case-with-comment-before-where-four-out.hs view
@@ -0,0 +1,14 @@+foo =+ case x of+ _ -> 1+ -- comment+ where+ -- comment 2+ x = 1++foo = case x of+ _ -> 1+ -- comment+ where+ -- comment 2+ x = 1
+ data/examples/declaration/value/function/case-with-comment-before-where-out.hs view
@@ -0,0 +1,14 @@+foo =+ case x of+ _ -> 1+ -- comment+ where+ -- comment 2+ x = 1++foo = case x of+ _ -> 1+ -- comment+ where+ -- comment 2+ x = 1
+ data/examples/declaration/value/function/case-with-comment-before-where.hs view
@@ -0,0 +1,14 @@+foo =+ case x of+ _ -> 1+ -- comment+ where+ -- comment 2+ x = 1++foo = case x of+ _ -> 1+ -- comment+ where+ -- comment 2+ x = 1
+ data/examples/declaration/value/function/do-multiline-with-case-four-out.hs view
@@ -0,0 +1,13 @@+handleInput :: IO ()+handleInput = do+ putStrLn "Enter command:"+ cmd <- getLine+ case cmd of+ "quit" -> putStrLn "Goodbye"+ "help" -> do+ putStrLn "Available commands:"+ putStrLn " quit - exit the program"+ putStrLn " help - show this message"+ _ -> do+ putStrLn $ "Unknown command: " ++ cmd+ handleInput
+ data/examples/declaration/value/function/do-multiline-with-case-out.hs view
@@ -0,0 +1,13 @@+handleInput :: IO ()+handleInput = do+ putStrLn "Enter command:"+ cmd <- getLine+ case cmd of+ "quit" -> putStrLn "Goodbye"+ "help" -> do+ putStrLn "Available commands:"+ putStrLn " quit - exit the program"+ putStrLn " help - show this message"+ _ -> do+ putStrLn $ "Unknown command: " ++ cmd+ handleInput
+ data/examples/declaration/value/function/do-multiline-with-case.hs view
@@ -0,0 +1,13 @@+handleInput :: IO ()+handleInput = do+ putStrLn "Enter command:"+ cmd <- getLine+ case cmd of+ "quit" -> putStrLn "Goodbye"+ "help" -> do+ putStrLn "Available commands:"+ putStrLn " quit - exit the program"+ putStrLn " help - show this message"+ _ -> do+ putStrLn $ "Unknown command: " ++ cmd+ handleInput
+ data/examples/declaration/value/function/do-single-line-case-guards-four-out.hs view
@@ -0,0 +1,2 @@+checkValue :: Int -> IO ()+checkValue n = do putStr "Value is: "; case () of { _ | n < 0 -> putStrLn "negative" | n == 0 -> putStrLn "zero" | otherwise -> putStrLn "positive" }
+ data/examples/declaration/value/function/do-single-line-case-guards-out.hs view
@@ -0,0 +1,2 @@+checkValue :: Int -> IO ()+checkValue n = do putStr "Value is: "; case () of { _ | n < 0 -> putStrLn "negative" | n == 0 -> putStrLn "zero" | otherwise -> putStrLn "positive" }
+ data/examples/declaration/value/function/do-single-line-case-guards.hs view
@@ -0,0 +1,2 @@+checkValue :: Int -> IO ()+checkValue n = do {putStr "Value is: "; case () of {_ | n < 0 -> putStrLn "negative" | n == 0 -> putStrLn "zero" | otherwise -> putStrLn "positive"}}
+ data/examples/declaration/value/function/do-single-line-lambda-case-four-out.hs view
@@ -0,0 +1,2 @@+processValue :: Maybe Int -> IO ()+processValue x = do putStrLn "Processing:"; \case { Just n -> print n; Nothing -> putStrLn "Empty" } x; putStrLn "Done"
+ data/examples/declaration/value/function/do-single-line-lambda-case-out.hs view
@@ -0,0 +1,2 @@+processValue :: Maybe Int -> IO ()+processValue x = do putStrLn "Processing:"; \case { Just n -> print n; Nothing -> putStrLn "Empty" } x; putStrLn "Done"
+ data/examples/declaration/value/function/do-single-line-lambda-case.hs view
@@ -0,0 +1,2 @@+processValue :: Maybe Int -> IO ()+processValue x = do {putStrLn "Processing:"; \case {Just n -> print n; Nothing -> putStrLn "Empty"} x; putStrLn "Done"}
+ data/examples/declaration/value/function/do-single-line-multiple-cases-four-out.hs view
@@ -0,0 +1,2 @@+processPair :: Maybe Int -> Maybe String -> IO ()+processPair x y = do case x of { Just n -> print n; Nothing -> putStrLn "No number" }; case y of { Just s -> putStrLn s; Nothing -> putStrLn "No string" }
+ data/examples/declaration/value/function/do-single-line-multiple-cases-out.hs view
@@ -0,0 +1,2 @@+processPair :: Maybe Int -> Maybe String -> IO ()+processPair x y = do case x of { Just n -> print n; Nothing -> putStrLn "No number" }; case y of { Just s -> putStrLn s; Nothing -> putStrLn "No string" }
+ data/examples/declaration/value/function/do-single-line-multiple-cases.hs view
@@ -0,0 +1,2 @@+processPair :: Maybe Int -> Maybe String -> IO ()+processPair x y = do {case x of {Just n -> print n; Nothing -> putStrLn "No number"}; case y of {Just s -> putStrLn s; Nothing -> putStrLn "No string"}}
+ data/examples/declaration/value/function/do-single-line-nested-case-four-out.hs view
@@ -0,0 +1,2 @@+nestedDo :: Either String Int -> IO ()+nestedDo e = do putStr "Start: "; case e of { Left s -> do { putStr "Error: "; putStrLn s }; Right n -> do { putStr "Value: "; print n } }; putStrLn "End"
+ data/examples/declaration/value/function/do-single-line-nested-case-out.hs view
@@ -0,0 +1,2 @@+nestedDo :: Either String Int -> IO ()+nestedDo e = do putStr "Start: "; case e of { Left s -> do { putStr "Error: "; putStrLn s }; Right n -> do { putStr "Value: "; print n } }; putStrLn "End"
+ data/examples/declaration/value/function/do-single-line-nested-case.hs view
@@ -0,0 +1,2 @@+nestedDo :: Either String Int -> IO ()+nestedDo e = do {putStr "Start: "; case e of {Left s -> do {putStr "Error: "; putStrLn s}; Right n -> do {putStr "Value: "; print n}}; putStrLn "End"}
+ data/examples/declaration/value/function/do-single-line-with-case-four-out.hs view
@@ -0,0 +1,2 @@+doGuessing :: (Ord t, Read t) => t -> IO ()+doGuessing num = do putStrLn "Enter your guess:"; guess <- getLine; case compare (read guess) num of { LT -> do { putStrLn "Too low!"; doGuessing num }; GT -> do { putStrLn "Too high!"; doGuessing num }; EQ -> putStrLn "You win!" }
+ data/examples/declaration/value/function/do-single-line-with-case-out.hs view
@@ -0,0 +1,2 @@+doGuessing :: (Ord t, Read t) => t -> IO ()+doGuessing num = do putStrLn "Enter your guess:"; guess <- getLine; case compare (read guess) num of { LT -> do { putStrLn "Too low!"; doGuessing num }; GT -> do { putStrLn "Too high!"; doGuessing num }; EQ -> putStrLn "You win!" }
+ data/examples/declaration/value/function/do-single-line-with-case.hs view
@@ -0,0 +1,2 @@+doGuessing :: (Ord t, Read t) => t -> IO ()+doGuessing num = do {putStrLn "Enter your guess:"; guess <- getLine; case compare (read guess) num of {LT -> do {putStrLn "Too low!"; doGuessing num}; GT -> do { putStrLn "Too high!"; doGuessing num}; EQ -> putStrLn "You win!"}}
data/examples/declaration/value/function/guards-four-out.hs view
@@ -10,3 +10,5 @@ quux :: Int -> Int quux x | x < 0 = x quux x = x++(a, b) | c = d
data/examples/declaration/value/function/guards-out.hs view
@@ -10,3 +10,5 @@ quux :: Int -> Int quux x | x < 0 = x quux x = x++(a, b) | c = d
data/examples/declaration/value/function/guards.hs view
@@ -10,3 +10,5 @@ quux :: Int -> Int quux x | x < 0 = x quux x = x++(a, b) | c = d
data/examples/declaration/value/function/infix/dollar-chains-1-four-out.hs view
@@ -13,9 +13,9 @@ throwIO (OrmoluCppEnabled path) foo =- bar $- baz $- quux+ bar+ $ baz+ $ quux x = case l of { A -> B } $
data/examples/declaration/value/function/infix/dollar-chains-1-out.hs view
@@ -13,9 +13,9 @@ throwIO (OrmoluCppEnabled path) foo =- bar $- baz $- quux+ bar+ $ baz+ $ quux x = case l of { A -> B } $
data/examples/declaration/value/function/infix/dollar-chains-3-four-out.hs view
@@ -1,11 +1,11 @@ ex1 =- f1 $- arg1 $- arg2 $- arg3+ f1+ $ arg1+ $ arg2+ $ arg3 ex3 =- f1 $- arg1 $- arg2 $- 1 + 3+ f1+ $ arg1+ $ arg2+ $ 1 + 3
data/examples/declaration/value/function/infix/dollar-chains-3-out.hs view
@@ -1,11 +1,11 @@ ex1 =- f1 $- arg1 $- arg2 $- arg3+ f1+ $ arg1+ $ arg2+ $ arg3 ex3 =- f1 $- arg1 $- arg2 $- 1 + 3+ f1+ $ arg1+ $ arg2+ $ 1 + 3
data/examples/declaration/value/function/infix/dollar-chains-4-four-out.hs view
@@ -1,5 +1,5 @@ ex2 =- f1 $- arg1 $- arg2 $- f2 arg3+ f1+ $ arg1+ $ arg2+ $ f2 arg3
data/examples/declaration/value/function/infix/dollar-chains-4-out.hs view
@@ -1,5 +1,5 @@ ex2 =- f1 $- arg1 $- arg2 $- f2 arg3+ f1+ $ arg1+ $ arg2+ $ f2 arg3
data/examples/declaration/value/function/infix/lenses-four-out.hs view
@@ -1,14 +1,14 @@ import Control.Lens lenses =- Just $- M.fromList $- "type" .= ("user.connection" :: Text)- # "connection" .= uc- # "user" .= case name of- Just n -> Just $ object ["name" .= n]- Nothing -> Nothing- # []+ Just+ $ M.fromList+ $ "type" .= ("user.connection" :: Text)+ # "connection" .= uc+ # "user" .= case name of+ Just n -> Just $ object ["name" .= n]+ Nothing -> Nothing+ # [] foo = a
data/examples/declaration/value/function/infix/lenses-out.hs view
@@ -1,14 +1,14 @@ import Control.Lens lenses =- Just $- M.fromList $- "type" .= ("user.connection" :: Text)- # "connection" .= uc- # "user" .= case name of- Just n -> Just $ object ["name" .= n]- Nothing -> Nothing- # []+ Just+ $ M.fromList+ $ "type" .= ("user.connection" :: Text)+ # "connection" .= uc+ # "user" .= case name of+ Just n -> Just $ object ["name" .= n]+ Nothing -> Nothing+ # [] foo = a
data/examples/declaration/value/function/infix/op-chain-r-eq-dollar-1-four-out.hs view
@@ -1,11 +1,11 @@ -- Right chain, $ case, 2 operators with p($) == p(b) n :: Int n =- 1 $- 2 $- 3 $- 4 `seq`- 5 $- 6 $- 7 $- 8+ 1+ $ 2+ $ 3+ $ 4+ `seq` 5+ $ 6+ $ 7+ $ 8
data/examples/declaration/value/function/infix/op-chain-r-eq-dollar-1-out.hs view
@@ -1,11 +1,11 @@ -- Right chain, $ case, 2 operators with p($) == p(b) n :: Int n =- 1 $- 2 $- 3 $- 4 `seq`- 5 $- 6 $- 7 $- 8+ 1+ $ 2+ $ 3+ $ 4+ `seq` 5+ $ 6+ $ 7+ $ 8
data/examples/declaration/value/function/infix/op-chain-r-eq-dollar-2-four-out.hs view
@@ -1,11 +1,11 @@ -- Right chain, $ case, 2 operators with p(a) == p($) p :: Int p =- 1 `seq`- 2 `seq`- 3 `seq`- 4 $- 5 `seq`- 6 `seq`- 7 `seq`- 8+ 1+ `seq` 2+ `seq` 3+ `seq` 4+ $ 5+ `seq` 6+ `seq` 7+ `seq` 8
data/examples/declaration/value/function/infix/op-chain-r-eq-dollar-2-out.hs view
@@ -1,11 +1,11 @@ -- Right chain, $ case, 2 operators with p(a) == p($) p :: Int p =- 1 `seq`- 2 `seq`- 3 `seq`- 4 $- 5 `seq`- 6 `seq`- 7 `seq`- 8+ 1+ `seq` 2+ `seq` 3+ `seq` 4+ $ 5+ `seq` 6+ `seq` 7+ `seq` 8
data/examples/declaration/value/function/infix/op-chain-r-s-dollar-four-out.hs view
@@ -1,7 +1,7 @@ -- Right chain, $ case, 1 operator type c :: Int c =- 1 $- 2 $- 3 $- 4+ 1+ $ 2+ $ 3+ $ 4
data/examples/declaration/value/function/infix/op-chain-r-s-dollar-out.hs view
@@ -1,7 +1,7 @@ -- Right chain, $ case, 1 operator type c :: Int c =- 1 $- 2 $- 3 $- 4+ 1+ $ 2+ $ 3+ $ 4
data/examples/declaration/value/function/infix/qualified-ops-four-out.hs view
@@ -1,9 +1,9 @@ lenses =- Just $- M.fromList $- "type" Foo..= ("user.connection" :: Text)- Bar.# "connection" Foo..= uc- Bar.# "user" Foo..= case name of- Just n -> Just $ object ["name" .= n]- Nothing -> Nothing- Bar.# []+ Just+ $ M.fromList+ $ "type" Foo..= ("user.connection" :: Text)+ Bar.# "connection" Foo..= uc+ Bar.# "user" Foo..= case name of+ Just n -> Just $ object ["name" .= n]+ Nothing -> Nothing+ Bar.# []
data/examples/declaration/value/function/infix/qualified-ops-out.hs view
@@ -1,9 +1,9 @@ lenses =- Just $- M.fromList $- "type" Foo..= ("user.connection" :: Text)- Bar.# "connection" Foo..= uc- Bar.# "user" Foo..= case name of- Just n -> Just $ object ["name" .= n]- Nothing -> Nothing- Bar.# []+ Just+ $ M.fromList+ $ "type" Foo..= ("user.connection" :: Text)+ Bar.# "connection" Foo..= uc+ Bar.# "user" Foo..= case name of+ Just n -> Just $ object ["name" .= n]+ Nothing -> Nothing+ Bar.# []
+ data/examples/declaration/value/function/linear-bindings-four-out.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE LinearTypes #-}++h x = g y+ where+ %1 y = f x++let %1 x = u in ()+let %Many (x, y) = u in ()+let %1 ~(x, y) = u in ()
+ data/examples/declaration/value/function/linear-bindings-out.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE LinearTypes #-}++h x = g y+ where+ %1 y = f x++let %1 x = u in ()+let %Many (x, y) = u in ()+let %1 ~(x, y) = u in ()
+ data/examples/declaration/value/function/linear-bindings.hs view
@@ -0,0 +1,9 @@+{-# Language LinearTypes #-}++h x = g y+ where+ %1 y = f x++let %1 x = u in ()+let %Many (x, y) = u in ()+let %1 ~(x, y) = u in ()
+ data/examples/declaration/value/function/multiline-strings-9-four-out.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE MultilineStrings #-}++multilineBlank =+ """+ 1+++++ 6+ """
+ data/examples/declaration/value/function/multiline-strings-9-out.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE MultilineStrings #-}++multilineBlank =+ """+ 1+++++ 6+ """
+ data/examples/declaration/value/function/multiline-strings-9.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE MultilineStrings #-}++multilineBlank =+ """+ 1+++++ 6+ """
data/examples/declaration/value/function/pattern/or-patterns-four-out.hs view
@@ -20,7 +20,8 @@ sane e = case e of 1 2- 3 -> a+ 3 ->+ a 4 5 6 -> b@@ -33,3 +34,10 @@ (D; E (Just _) Nothing) -> 4 F -> 5++food+ foo@( A;+ B;+ C+ ) = Just foo+food _ = Nothing
data/examples/declaration/value/function/pattern/or-patterns-out.hs view
@@ -21,7 +21,8 @@ sane e = case e of 1 2- 3 -> a+ 3 ->+ a 4 5 6 -> b@@ -34,3 +35,10 @@ (D; E (Just _) Nothing) -> 4 F -> 5++food+ foo@( A;+ B;+ C+ ) = Just foo+food _ = Nothing
data/examples/declaration/value/function/pattern/or-patterns.hs view
@@ -20,7 +20,8 @@ sane e = case e of 1 2- 3 -> a+ 3 ->+ a 4 5;6 -> b 7;8 -> c@@ -31,3 +32,8 @@ (D; E (Just _) Nothing) -> 4 F -> 5++food foo@(A;+ B;+ C) = Just foo+food _ = Nothing
data/examples/declaration/value/function/strings-four-out.hs view
@@ -8,3 +8,5 @@ \baz" weirdGap = "\65\ \0"++weirdEscape = "\^\ "
data/examples/declaration/value/function/strings-out.hs view
@@ -10,3 +10,5 @@ \baz" weirdGap = "\65\ \0"++weirdEscape = "\^\ "
data/examples/declaration/value/function/strings.hs view
@@ -7,3 +7,5 @@ \baz" weirdGap = "\65\ \0"++weirdEscape = "\^\ "
data/examples/declaration/value/pattern-synonyms/explicitely-bidirectional-four-out.hs view
@@ -3,19 +3,19 @@ pattern P a <- C a where P a = C a pattern HeadC x <- x : xs- where- HeadC x = [x]+ where+ HeadC x = [x] pattern HeadC' x <- x : xs- where- HeadC' x = [x]+ where+ HeadC' x = [x] pattern Simple <- "Simple"- where- Simple = "Complicated"+ where+ Simple = "Complicated" pattern a :< b <- (a, b)- where- a :< b = (a, b)+ where+ a :< b = (a, b)
+ data/examples/fixity/megaparsec-alternative-four-out.hs view
@@ -0,0 +1,8 @@+module MegaparsecExample where++import Text.Megaparsec++pValue =+ Object <$> parseObjectBody+ <|> Array <$> parseArrayBody+ <|> String <$> parseStringBody
+ data/examples/fixity/megaparsec-alternative-out.hs view
@@ -0,0 +1,8 @@+module MegaparsecExample where++import Text.Megaparsec++pValue =+ Object <$> parseObjectBody+ <|> Array <$> parseArrayBody+ <|> String <$> parseStringBody
+ data/examples/fixity/megaparsec-alternative.hs view
@@ -0,0 +1,8 @@+module MegaparsecExample where++import Text.Megaparsec++pValue =+ Object <$> parseObjectBody+ <|> Array <$> parseArrayBody+ <|> String <$> parseStringBody
+ data/examples/fixity/optics-mixed-four-out.hs view
@@ -0,0 +1,8 @@+module OpticsExample where++import Optics++updated =+ record+ & fieldLens % subFieldLens .~ someValue+ & otherLens %~ someTransformationFunctionApplied
+ data/examples/fixity/optics-mixed-out.hs view
@@ -0,0 +1,8 @@+module OpticsExample where++import Optics++updated =+ record+ & fieldLens % subFieldLens .~ someValue+ & otherLens %~ someTransformationFunctionApplied
+ data/examples/fixity/optics-mixed.hs view
@@ -0,0 +1,8 @@+module OpticsExample where++import Optics++updated =+ record+ & fieldLens % subFieldLens .~ someValue+ & otherLens %~ someTransformationFunctionApplied
+ data/examples/fixity/relude-default-operator-chain-four-out.hs view
@@ -0,0 +1,10 @@+module ReludeChainExample where++import Relude++resolveValue =+ primarySource+ ?: secondarySource+ ?: tertiarySource+ ?: quaternarySource+ ?: finalFallbackValue
+ data/examples/fixity/relude-default-operator-chain-out.hs view
@@ -0,0 +1,10 @@+module ReludeChainExample where++import Relude++resolveValue =+ primarySource+ ?: secondarySource+ ?: tertiarySource+ ?: quaternarySource+ ?: finalFallbackValue
+ data/examples/fixity/relude-default-operator-chain.hs view
@@ -0,0 +1,10 @@+module ReludeChainExample where++import Relude++resolveValue =+ primarySource+ ?: secondarySource+ ?: tertiarySource+ ?: quaternarySource+ ?: finalFallbackValue
+ data/examples/fixity/relude-default-operator-four-out.hs view
@@ -0,0 +1,7 @@+module ReludeExample where++import Relude++config =+ lookupOptionalSetting environment ?:+ defaultConfigurationValue
+ data/examples/fixity/relude-default-operator-out.hs view
@@ -0,0 +1,7 @@+module ReludeExample where++import Relude++config =+ lookupOptionalSetting environment ?:+ defaultConfigurationValue
+ data/examples/fixity/relude-default-operator.hs view
@@ -0,0 +1,7 @@+module ReludeExample where++import Relude++config =+ lookupOptionalSetting environment+ ?: defaultConfigurationValue
+ data/examples/fixity/rio-ampersand-four-out.hs view
@@ -0,0 +1,7 @@+module RioExample where++import RIO++message =+ greetingText <> userNameText+ & Text.strip
+ data/examples/fixity/rio-ampersand-out.hs view
@@ -0,0 +1,7 @@+module RioExample where++import RIO++message =+ greetingText <> userNameText+ & Text.strip
+ data/examples/fixity/rio-ampersand.hs view
@@ -0,0 +1,7 @@+module RioExample where++import RIO++message =+ greetingText <> userNameText+ & Text.strip
+ data/examples/fixity/rio-deepseq-four-out.hs view
@@ -0,0 +1,7 @@+module RioDeepseqExample where++import RIO++result =+ forceEvaluationOfBigStructure `deepseq`+ continueWithNextStep
+ data/examples/fixity/rio-deepseq-out.hs view
@@ -0,0 +1,7 @@+module RioDeepseqExample where++import RIO++result =+ forceEvaluationOfBigStructure `deepseq`+ continueWithNextStep
+ data/examples/fixity/rio-deepseq.hs view
@@ -0,0 +1,7 @@+module RioDeepseqExample where++import RIO++result =+ forceEvaluationOfBigStructure+ `deepseq` continueWithNextStep
+ data/examples/import/data-four-out.hs view
@@ -0,0 +1,3 @@+module Bar (data P, T (data P), data f) where++import N (T (data P), data P, data f)
+ data/examples/import/data-out.hs view
@@ -0,0 +1,3 @@+module Bar (data P, T (data P), data f) where++import N (T (data P), data P, data f)
+ data/examples/import/data.hs view
@@ -0,0 +1,6 @@++module Bar (data P, T(data P), data f) where++import N (data P)+import N (T(data P))+import N (data f)
+ data/examples/import/explicit-level-imports-four-out.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE ExplicitLevelImports #-}++import A splice+import {-# SOURCE #-} safe qualified A splice as QA hiding (a, b, c, d, e, f)+import quote qualified B as QB+import qualified C splice as SC+import qualified D splice+import Data.ByteString (e)+import Data.ByteString.Lazy quote (d)+import splice Data.Text (a, b, c)+import PyF ()+import splice PyF (fmt, tmf)+import quote PyF (abc)
+ data/examples/import/explicit-level-imports-out.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE ExplicitLevelImports #-}++import A splice+import {-# SOURCE #-} safe qualified A splice as QA hiding (a, b, c, d, e, f)+import quote qualified B as QB+import qualified C splice as SC+import qualified D splice+import Data.ByteString (e)+import Data.ByteString.Lazy quote (d)+import splice Data.Text (a, b, c)+import PyF ()+import splice PyF (fmt, tmf)+import quote PyF (abc)
+ data/examples/import/explicit-level-imports-qualified-post-four-out.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE ExplicitLevelImports #-}+{-# LANGUAGE ImportQualifiedPost #-}++import quote A qualified as QA+import B quote qualified as QB
+ data/examples/import/explicit-level-imports-qualified-post-out.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE ExplicitLevelImports #-}+{-# LANGUAGE ImportQualifiedPost #-}++import quote A qualified as QA+import B quote qualified as QB
+ data/examples/import/explicit-level-imports-qualified-post.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE ExplicitLevelImports #-}+{-# LANGUAGE ImportQualifiedPost #-}++import qualified B quote as QB+import quote qualified A as QA
+ data/examples/import/explicit-level-imports.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE ExplicitLevelImports #-}++import splice Data.Text (a, b, c)+import Data.ByteString.Lazy quote (d)+import Data.ByteString (e)+import {-# SOURCE #-} safe qualified A splice as QA hiding (a, b, c, d, e, f)+import quote qualified B as QB+import qualified C splice as SC+import A splice+import qualified D splice+import quote PyF (abc)+import splice PyF (fmt)+import splice PyF (tmf)+import PyF ()
+ data/examples/import/implicit-prelude-package-four-out.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE PackageImports #-}++import "base" Control.Applicative (Alternative, (<|>))+import "base" Data.Maybe (Maybe (Nothing), maybe)+import "base" System.IO (IO)+import "yaya" Yaya.Fold (ana, cata)+import "base" Prelude ((+))
+ data/examples/import/implicit-prelude-package-out.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE PackageImports #-}++import "base" Control.Applicative (Alternative, (<|>))+import "base" Data.Maybe (Maybe (Nothing), maybe)+import "base" System.IO (IO)+import "yaya" Yaya.Fold (ana, cata)+import "base" Prelude ((+))
+ data/examples/import/implicit-prelude-package.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE PackageImports #-}++import "base" System.IO (IO)+import "base" Prelude ((+))+import "yaya" Yaya.Fold (ana, cata)+import "base" Control.Applicative (Alternative, (<|>))+import "base" Data.Maybe (Maybe (Nothing), maybe)
+ data/examples/import/no-implicit-prelude-four-out.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE NoImplicitPrelude #-}++import Control.Applicative (Alternative, (<|>))+import Data.Maybe (Maybe (Nothing), maybe)+import Prelude ((+))+import System.IO (IO)
+ data/examples/import/no-implicit-prelude-out.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE NoImplicitPrelude #-}++import Control.Applicative (Alternative, (<|>))+import Data.Maybe (Maybe (Nothing), maybe)+import Prelude ((+))+import System.IO (IO)
+ data/examples/import/no-implicit-prelude-package-four-out.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE NoImplicitPrelude #-}++import "base" Control.Applicative (Alternative, (<|>))+import "base" Data.Maybe (Maybe (Nothing), maybe)+import "base" Prelude ((+))+import "base" System.IO (IO)+import "yaya" Yaya.Fold (ana, cata)
+ data/examples/import/no-implicit-prelude-package-out.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE NoImplicitPrelude #-}++import "base" Control.Applicative (Alternative, (<|>))+import "base" Data.Maybe (Maybe (Nothing), maybe)+import "base" Prelude ((+))+import "base" System.IO (IO)+import "yaya" Yaya.Fold (ana, cata)
+ data/examples/import/no-implicit-prelude-package.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE PackageImports #-}++import "base" System.IO (IO)+import "base" Prelude ((+))+import "yaya" Yaya.Fold (ana, cata)+import "base" Control.Applicative (Alternative, (<|>))+import "base" Data.Maybe (Maybe (Nothing), maybe)
+ data/examples/import/no-implicit-prelude.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE NoImplicitPrelude #-}++import System.IO (IO)+import Prelude ((+))+import Control.Applicative (Alternative, (<|>))+import Data.Maybe (Maybe (Nothing), maybe)
+ data/examples/module-header/block-haddock-in-export-list-four-out.hs view
@@ -0,0 +1,4 @@+module Foo (+ -- | asdf+ foo,+) where
+ data/examples/module-header/block-haddock-in-export-list-out.hs view
@@ -0,0 +1,5 @@+module Foo+ ( -- | asdf+ foo,+ )+where
+ data/examples/module-header/block-haddock-in-export-list.hs view
@@ -0,0 +1,1 @@+module Foo ({- | asdf -} foo) where
+ data/examples/other/comment-trailing-blank-line-do-four-out.hs view
@@ -0,0 +1,4 @@+doBlock = do+ a --++ b
+ data/examples/other/comment-trailing-blank-line-do-out.hs view
@@ -0,0 +1,4 @@+doBlock = do+ a --++ b
+ data/examples/other/comment-trailing-blank-line-do.hs view
@@ -0,0 +1,4 @@+doBlock = do+ a --++ b
+ data/examples/other/comment-trailing-blank-line-let-four-out.hs view
@@ -0,0 +1,5 @@+letBlock =+ let a = a --++ b = b+ in c
+ data/examples/other/comment-trailing-blank-line-let-out.hs view
@@ -0,0 +1,5 @@+letBlock =+ let a = a --++ b = b+ in c
+ data/examples/other/comment-trailing-blank-line-let.hs view
@@ -0,0 +1,5 @@+letBlock =+ let a = a --++ b = b+ in c
+ data/examples/other/comment-trailing-blank-line-variants-four-out.hs view
@@ -0,0 +1,22 @@+textComment = do+ a -- some text++ b++blockComment = do+ a {- foo -}++ b++twoComments = do+ a --++ --++ bar++adjacentThenBlank = do+ a --+ --++ bar
+ data/examples/other/comment-trailing-blank-line-variants-out.hs view
@@ -0,0 +1,22 @@+textComment = do+ a -- some text++ b++blockComment = do+ a {- foo -}++ b++twoComments = do+ a --++ --++ bar++adjacentThenBlank = do+ a --+ --++ bar
+ data/examples/other/comment-trailing-blank-line-variants.hs view
@@ -0,0 +1,22 @@+textComment = do+ a -- some text++ b++blockComment = do+ a {- foo -}++ b++twoComments = do+ a --++ --++ bar++adjacentThenBlank = do+ a --+ --++ bar
+ data/examples/other/comment-trailing-blank-line-where-four-out.hs view
@@ -0,0 +1,5 @@+whereBlock = foo+ where+ a = a --++ b = b
+ data/examples/other/comment-trailing-blank-line-where-out.hs view
@@ -0,0 +1,5 @@+whereBlock = foo+ where+ a = a --++ b = b
+ data/examples/other/comment-trailing-blank-line-where.hs view
@@ -0,0 +1,5 @@+whereBlock = foo+ where+ a = a --++ b = b
+ data/examples/other/comment-trailing-no-blank-line-four-out.hs view
@@ -0,0 +1,25 @@+noBlankAfterComment = do+ a --+ b++noBlankTextComment = do+ a -- some text+ b++noBlankBlockComment = do+ a {- foo -}+ b++blockCommentFollowedByExpr = do+ a {- foo -} 1+ b++whereNoBlank = foo+ where+ a = a --+ b = b++letNoBlank =+ let a = a --+ b = b+ in c
+ data/examples/other/comment-trailing-no-blank-line-out.hs view
@@ -0,0 +1,25 @@+noBlankAfterComment = do+ a --+ b++noBlankTextComment = do+ a -- some text+ b++noBlankBlockComment = do+ a {- foo -}+ b++blockCommentFollowedByExpr = do+ a {- foo -} 1+ b++whereNoBlank = foo+ where+ a = a --+ b = b++letNoBlank =+ let a = a --+ b = b+ in c
+ data/examples/other/comment-trailing-no-blank-line.hs view
@@ -0,0 +1,25 @@+noBlankAfterComment = do+ a --+ b++noBlankTextComment = do+ a -- some text+ b++noBlankBlockComment = do+ a {- foo -}+ b++blockCommentFollowedByExpr = do+ a {- foo -} 1+ b++whereNoBlank = foo+ where+ a = a --+ b = b++letNoBlank =+ let a = a --+ b = b+ in c
data/examples/other/empty-forall-four-out.hs view
@@ -12,7 +12,7 @@ forall. T x = x {-# RULES-"r"+"r" forall. r a = () #-}
data/examples/other/empty-forall-out.hs view
@@ -12,7 +12,7 @@ forall. T x = x {-# RULES-"r"+"r" forall. r a = () #-}
data/fourmolu/haddock-style/output-multi_line-module=multi_line.hs view
@@ -37,6 +37,6 @@ {- | This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line-module=multi_line_compact.hs view
@@ -37,6 +37,6 @@ {- | This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line-module=single_line.hs view
@@ -36,6 +36,6 @@ {- | This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line.hs view
@@ -37,6 +37,6 @@ {- | This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line_compact-module=multi_line.hs view
@@ -37,6 +37,6 @@ {-| This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line_compact-module=multi_line_compact.hs view
@@ -37,6 +37,6 @@ {-| This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line_compact-module=single_line.hs view
@@ -36,6 +36,6 @@ {-| This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line_compact.hs view
@@ -37,6 +37,6 @@ {-| This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/import-grouping/input.hs view
@@ -1,13 +1,16 @@ module Main where +import Data.Either import Data.Text (Text) import Data.Maybe (maybe) import qualified Data.Text import Control.Monad (Monad (..))+import Data.Functor import qualified System.IO as SIO import SomeInternal.Module1 (anotherDefinition, someDefinition) import qualified SomeInternal.Module2 as Mod2 import Text.Printf (printf) import qualified SomeModule+import SomeInternal.Module2
data/fourmolu/import-grouping/output-by_qualified.hs view
@@ -1,9 +1,12 @@ module Main where import Control.Monad (Monad (..))+import Data.Either+import Data.Functor import Data.Maybe (maybe) import Data.Text (Text) import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import Text.Printf (printf) import qualified Data.Text
data/fourmolu/import-grouping/output-by_scope.hs view
@@ -1,6 +1,8 @@ module Main where import Control.Monad (Monad (..))+import Data.Either+import Data.Functor import Data.Maybe (maybe) import Data.Text (Text) import qualified Data.Text@@ -9,4 +11,5 @@ import Text.Printf (printf) import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import qualified SomeInternal.Module2 as Mod2
data/fourmolu/import-grouping/output-by_scope_then_qualified.hs view
@@ -1,6 +1,8 @@ module Main where import Control.Monad (Monad (..))+import Data.Either+import Data.Functor import Data.Maybe (maybe) import Data.Text (Text) import Text.Printf (printf)@@ -10,5 +12,6 @@ import qualified System.IO as SIO import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import qualified SomeInternal.Module2 as Mod2
data/fourmolu/import-grouping/output-custom.hs view
@@ -1,5 +1,9 @@ module Main where +import Data.Either+import Data.Functor+import SomeInternal.Module2+ import Data.Text (Text) import qualified Data.Text
data/fourmolu/import-grouping/output-preserve.hs view
@@ -1,12 +1,15 @@ module Main where +import Data.Either import Data.Maybe (maybe) import Data.Text (Text) import Control.Monad (Monad (..))+import Data.Functor import qualified Data.Text import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import qualified SomeInternal.Module2 as Mod2 import qualified SomeModule import qualified System.IO as SIO
data/fourmolu/import-grouping/output-single.hs view
@@ -1,10 +1,13 @@ module Main where import Control.Monad (Monad (..))+import Data.Either+import Data.Functor import Data.Maybe (maybe) import Data.Text (Text) import qualified Data.Text import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import qualified SomeInternal.Module2 as Mod2 import qualified SomeModule import qualified System.IO as SIO
data/fourmolu/indentation/input.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo ( asdf, bar,@@ -21,3 +23,7 @@ class Foo a where foo :: a -> Int++pattern HeadC x <- x : xs+ where+ HeadC x = [x]
data/fourmolu/indentation/output-2-indent_wheres.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo ( asdf, bar,@@ -21,3 +23,7 @@ class Foo a where foo :: a -> Int++pattern HeadC x <- x : xs+ where+ HeadC x = [x]
data/fourmolu/indentation/output-2.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo ( asdf, bar,@@ -21,3 +23,7 @@ class Foo a where foo :: a -> Int++pattern HeadC x <- x : xs+ where+ HeadC x = [x]
data/fourmolu/indentation/output-3-indent_wheres.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo ( asdf, bar,@@ -21,3 +23,7 @@ class Foo a where foo :: a -> Int++pattern HeadC x <- x : xs+ where+ HeadC x = [x]
data/fourmolu/indentation/output-3.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo ( asdf, bar,@@ -21,3 +23,7 @@ class Foo a where foo :: a -> Int++pattern HeadC x <- x : xs+ where+ HeadC x = [x]
data/fourmolu/indentation/output-4-indent_wheres.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo ( asdf, bar,@@ -21,3 +23,7 @@ class Foo a where foo :: a -> Int++pattern HeadC x <- x : xs+ where+ HeadC x = [x]
data/fourmolu/indentation/output-4.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo ( asdf, bar,@@ -21,3 +23,7 @@ class Foo a where foo :: a -> Int++pattern HeadC x <- x : xs+ where+ HeadC x = [x]
+ data/fourmolu/record-style/input.hs view
@@ -0,0 +1,24 @@+data Foo = Foo+ { a :: Int+ , b :: Int+ , c :: Int+ }++-- Test nesting+data Client = PremiumClient { name :: String,+ address :: String, aFieldWithAnUnneccessarilyLongFieldName :: String }+ -- And some comment here as well+ | NotSoPremiumClient { name :: String,+ address :: String, aFieldWithAnUnneccessarilyLongFieldName :: String }++x :: Foo -> [Int]+x Foo{ a = possiblyLongName,+ b = anotherLongName,+ c = longNameAsWell+ } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell = Foo{ a = possiblyLongName,+ b = anotherLongName,+ c = longNameAsWell+ }
+ data/fourmolu/record-style/output-aligned-leading.hs view
@@ -0,0 +1,35 @@+data Foo = Foo+ { a :: Int+ , b :: Int+ , c :: Int+ }++-- Test nesting+data Client+ = PremiumClient+ { name :: String+ , address :: String+ , aFieldWithAnUnneccessarilyLongFieldName :: String+ }+ | -- And some comment here as well+ NotSoPremiumClient+ { name :: String+ , address :: String+ , aFieldWithAnUnneccessarilyLongFieldName :: String+ }++x :: Foo -> [Int]+x+ Foo+ { a = possiblyLongName+ , b = anotherLongName+ , c = longNameAsWell+ } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell =+ Foo+ { a = possiblyLongName+ , b = anotherLongName+ , c = longNameAsWell+ }
+ data/fourmolu/record-style/output-aligned-trailing.hs view
@@ -0,0 +1,35 @@+data Foo = Foo+ { a :: Int,+ b :: Int,+ c :: Int+ }++-- Test nesting+data Client+ = PremiumClient+ { name :: String,+ address :: String,+ aFieldWithAnUnneccessarilyLongFieldName :: String+ }+ | -- And some comment here as well+ NotSoPremiumClient+ { name :: String,+ address :: String,+ aFieldWithAnUnneccessarilyLongFieldName :: String+ }++x :: Foo -> [Int]+x+ Foo+ { a = possiblyLongName,+ b = anotherLongName,+ c = longNameAsWell+ } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell =+ Foo+ { a = possiblyLongName,+ b = anotherLongName,+ c = longNameAsWell+ }
+ data/fourmolu/record-style/output-knr-leading.hs view
@@ -0,0 +1,35 @@+data Foo = Foo {+ a :: Int+ , b :: Int+ , c :: Int+ }++-- Test nesting+data Client+ = PremiumClient {+ name :: String+ , address :: String+ , aFieldWithAnUnneccessarilyLongFieldName :: String+ }+ | -- And some comment here as well+ NotSoPremiumClient {+ name :: String+ , address :: String+ , aFieldWithAnUnneccessarilyLongFieldName :: String+ }++x :: Foo -> [Int]+x+ Foo {+ a = possiblyLongName+ , b = anotherLongName+ , c = longNameAsWell+ } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell =+ Foo {+ a = possiblyLongName+ , b = anotherLongName+ , c = longNameAsWell+ }
+ data/fourmolu/record-style/output-knr-trailing.hs view
@@ -0,0 +1,35 @@+data Foo = Foo {+ a :: Int,+ b :: Int,+ c :: Int+}++-- Test nesting+data Client+ = PremiumClient {+ name :: String,+ address :: String,+ aFieldWithAnUnneccessarilyLongFieldName :: String+ }+ | -- And some comment here as well+ NotSoPremiumClient {+ name :: String,+ address :: String,+ aFieldWithAnUnneccessarilyLongFieldName :: String+ }++x :: Foo -> [Int]+x+ Foo {+ a = possiblyLongName,+ b = anotherLongName,+ c = longNameAsWell+ } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell =+ Foo {+ a = possiblyLongName,+ b = anotherLongName,+ c = longNameAsWell+ }
data/fourmolu/trailing-section-operators/output-True.hs view
@@ -3,11 +3,11 @@ someShortFunction $ someShortFunction $ \x -> do putStrLn x -someLongFunction $- someLongFunction $- someLongFunction $- someLongFunction $- someLongFunction x+someLongFunction+ $ someLongFunction+ $ someLongFunction+ $ someLongFunction+ $ someLongFunction x someLongFunction $ someLongFunction $
data/fourmolu/unicode-syntax/output-always.hs view
@@ -55,8 +55,8 @@ deconstruct (MkT1 x) = x pattern HeadC x ← x : xs- where- HeadC x = [x]+ where+ HeadC x = [x] pattern Point ∷ Int → Int → (Int, Int) pattern Point{x, y} = (x, y)
data/fourmolu/unicode-syntax/output-detect.hs view
@@ -55,8 +55,8 @@ deconstruct (MkT1 x) = x pattern HeadC x ← x : xs- where- HeadC x = [x]+ where+ HeadC x = [x] pattern Point ∷ Int → Int → (Int, Int) pattern Point{x, y} = (x, y)
data/fourmolu/unicode-syntax/output-never.hs view
@@ -55,8 +55,8 @@ deconstruct (MkT1 x) = x pattern HeadC x <- x : xs- where- HeadC x = [x]+ where+ HeadC x = [x] pattern Point :: Int -> Int -> (Int, Int) pattern Point{x, y} = (x, y)
extract-hackage-info/hackage-info.bin view
binary file changed (1002933 → 1243493 bytes)
fixity-tests/test-1-no-extra-info-expected.hs view
@@ -1,12 +1,12 @@ lenses =- Just $- M.fromList $- "type"- .= ("user.connection" :: Text)- # "connection"- .= uc- # "user"- .= case name of- Just n -> Just $ object ["name" .= n]- Nothing -> Nothing- # []+ Just+ $ M.fromList+ $ "type"+ .= ("user.connection" :: Text)+ # "connection"+ .= uc+ # "user"+ .= case name of+ Just n -> Just $ object ["name" .= n]+ Nothing -> Nothing+ # []
fixity-tests/test-1-with-fixity-info-expected.hs view
@@ -1,9 +1,9 @@ lenses =- Just $- M.fromList $- "type" .= ("user.connection" :: Text)- # "connection" .= uc- # "user" .= case name of- Just n -> Just $ object ["name" .= n]- Nothing -> Nothing- # []+ Just+ $ M.fromList+ $ "type" .= ("user.connection" :: Text)+ # "connection" .= uc+ # "user" .= case name of+ Just n -> Just $ object ["name" .= n]+ Nothing -> Nothing+ # []
fourmolu.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: fourmolu-version: 0.19.0.1+version: 0.20.1.0 license: BSD-3-Clause license-file: LICENSE.md maintainer:@@ -8,9 +8,9 @@ George Thomas <georgefsthomas@gmail.com> Brandon Chinn <brandonchinn178@gmail.com> tested-with:- ghc ==9.8.4- ghc ==9.10.1- ghc ==9.12.1+ ghc ==9.10.2+ ghc ==9.12.2+ ghc ==9.14.1 homepage: https://github.com/fourmolu/fourmolu bug-reports: https://github.com/fourmolu/fourmolu/issues@@ -84,6 +84,7 @@ Ormolu.Printer.Meat.Module Ormolu.Printer.Meat.Pragma Ormolu.Printer.Meat.Type+ Ormolu.Printer.Meat.Type.Function Ormolu.Printer.Operators Ormolu.Printer.SpanStream Ormolu.Processing.Common@@ -100,7 +101,7 @@ other-modules: GHC.DynFlags default-language: GHC2021 build-depends:- Cabal-syntax >=3.14 && <3.15,+ Cabal-syntax >=3.16 && <3.17, Diff >=0.4 && <2, MemoTrie >=0.6 && <0.7, ansi-terminal >=0.10 && <1.2,@@ -109,11 +110,11 @@ binary >=0.8 && <0.9, bytestring >=0.2 && <0.13, choice >=0.2.4.1 && <0.3,- containers >=0.5 && <0.8,+ containers >=0.5 && <0.9, directory ^>=1.3, file-embed >=0.0.15 && <0.1, filepath >=1.2 && <1.6,- ghc-lib-parser >=9.12 && <9.13,+ ghc-lib-parser >=9.14 && <9.15, megaparsec >=9, mtl >=2 && <3, syb >=0.7 && <0.8,@@ -152,13 +153,13 @@ autogen-modules: Paths_fourmolu default-language: GHC2021 build-depends:- Cabal-syntax >=3.14 && <3.15,+ Cabal-syntax >=3.16 && <3.17, base >=4.12 && <5,- containers >=0.5 && <0.8,+ containers >=0.5 && <0.9, directory ^>=1.3, filepath >=1.2 && <1.6,- ghc-lib-parser >=9.12 && <9.13,- optparse-applicative >=0.14 && <0.19,+ ghc-lib-parser >=9.14 && <9.15,+ optparse-applicative >=0.14 && <0.20, text >=2.1 && <3, th-env >=0.1.1 && <0.2, unliftio >=0.2.10 && <0.3,@@ -210,14 +211,14 @@ default-language: GHC2021 build-depends:- Cabal-syntax >=3.14 && <3.15,+ Cabal-syntax >=3.16 && <3.17, QuickCheck >=2.14, base >=4.14 && <5, choice >=0.2.4.1 && <0.3,- containers >=0.5 && <0.8,+ containers >=0.5 && <0.9, directory ^>=1.3, filepath >=1.2 && <1.6,- ghc-lib-parser >=9.12 && <9.13,+ ghc-lib-parser >=9.14 && <9.15, hspec >=2 && <3, hspec-megaparsec >=2.2, megaparsec >=9,
fourmolu.yaml view
@@ -5,6 +5,7 @@ column-limit: none function-arrows: trailing comma-style: trailing+record-style: aligned import-export-style: trailing import-grouping: legacy indent-wheres: true
src/GHC/DynFlags.hs view
@@ -7,10 +7,12 @@ ) where +import GHC.Data.FastString import GHC.Driver.Session import GHC.Platform import GHC.Settings import GHC.Settings.Config+import GHC.Unit.Types import GHC.Utils.Fingerprint fakeSettings :: Settings@@ -43,6 +45,10 @@ platform_constants = Nothing }, sPlatformMisc = PlatformMisc {},+ sUnitSettings =+ UnitSettings+ { unitSettings_baseUnitId = UnitId $ fsLit "ormolu"+ }, sToolSettings = ToolSettings { toolSettings_opt_P_fingerprint = fingerprint0,
src/Ormolu.hs view
@@ -47,6 +47,7 @@ import Control.Exception import Control.Monad import Control.Monad.IO.Class (MonadIO (..))+import Data.Choice qualified as Choice import Data.Map.Strict qualified as Map import Data.Maybe (fromMaybe) import Data.Set qualified as Set@@ -118,7 +119,7 @@ -- when we try to parse the rendered code back, inside of GHC monad -- wrapper which will lead to error messages presenting the exceptions as -- GHC bugs.- let !formattedText = printSnippets (cfgDebug cfg) result0 $ cfgPrinterOpts cfgWithIndices+ let !formattedText = printSnippets (Choice.fromBool (cfgDebug cfg)) result0 $ cfgPrinterOpts cfgWithIndices when (not (cfgUnsafe cfg) || cfgCheckIdempotence cfg) $ do -- Parse the result of pretty-printing again and make sure that AST -- is the same as AST of original snippet module span positions.@@ -144,7 +145,7 @@ -- Try re-formatting the formatted result to check if we get exactly -- the same output. when (cfgCheckIdempotence cfg) . liftIO $- let reformattedText = printSnippets (cfgDebug cfg) result1 $ cfgPrinterOpts cfgWithIndices+ let reformattedText = printSnippets (Choice.fromBool (cfgDebug cfg)) result1 $ cfgPrinterOpts cfgWithIndices in case diffText formattedText reformattedText path of Nothing -> return () Just diff -> throwIO (OrmoluNonIdempotentOutput diff)
src/Ormolu/Config.hs view
@@ -35,6 +35,7 @@ fillMissingPrinterOpts, resolvePrinterOpts, CommaStyle (..),+ RecordStyle (..), FunctionArrowsStyle (..), HaddockPrintStyle (..), HaddockPrintStyleModule (..),@@ -43,12 +44,13 @@ ImportGrouping (..), ImportGroup (..), ImportGroupRule (..),- ImportModuleMatcher (..), ImportRulePriority (..), matchAllRulePriority, matchLocalRulePriority, defaultImportRulePriority,+ ImportListMatcher (..), QualifiedImportMatcher (..),+ ImportScopeMatcher (..), LetStyle (..), InStyle (..), IfStyle (..),
src/Ormolu/Config/Gen.hs view
@@ -9,6 +9,7 @@ module Ormolu.Config.Gen ( PrinterOpts (..) , CommaStyle (..)+ , RecordStyle (..) , FunctionArrowsStyle (..) , HaddockPrintStyle (..) , HaddockPrintStyleModule (..)@@ -54,6 +55,8 @@ poFunctionArrows :: f FunctionArrowsStyle , -- | How to place commas in multi-line lists, records, etc. poCommaStyle :: f CommaStyle+ , -- | How to place braces in records+ poRecordStyle :: f RecordStyle , -- | Styling of import/export lists poImportExportStyle :: f ImportExportStyle , -- | Rules for grouping import declarations@@ -102,6 +105,7 @@ , poColumnLimit = Nothing , poFunctionArrows = Nothing , poCommaStyle = Nothing+ , poRecordStyle = Nothing , poImportExportStyle = Nothing , poImportGrouping = Nothing , poIndentWheres = Nothing@@ -130,6 +134,7 @@ , poColumnLimit = pure NoLimit , poFunctionArrows = pure TrailingArrows , poCommaStyle = pure Leading+ , poRecordStyle = pure RecordStyleAligned , poImportExportStyle = pure ImportExportDiffFriendly , poImportGrouping = pure ImportGroupLegacy , poIndentWheres = pure False@@ -165,6 +170,7 @@ , poColumnLimit = maybe (poColumnLimit p2) pure (poColumnLimit p1) , poFunctionArrows = maybe (poFunctionArrows p2) pure (poFunctionArrows p1) , poCommaStyle = maybe (poCommaStyle p2) pure (poCommaStyle p1)+ , poRecordStyle = maybe (poRecordStyle p2) pure (poRecordStyle p1) , poImportExportStyle = maybe (poImportExportStyle p2) pure (poImportExportStyle p1) , poImportGrouping = maybe (poImportGrouping p2) pure (poImportGrouping p1) , poIndentWheres = maybe (poIndentWheres p2) pure (poIndentWheres p1)@@ -209,6 +215,10 @@ "How to place commas in multi-line lists, records, etc. (choices: \"leading\" or \"trailing\") (default: leading)" "OPTION" <*> f+ "record-style"+ "How to place braces in records (choices: \"aligned\" or \"knr\") (default: aligned)"+ "OPTION"+ <*> f "import-export-style" "Styling of import/export lists (choices: \"leading\", \"trailing\", or \"diff-friendly\") (default: diff-friendly)" "OPTION"@@ -295,6 +305,7 @@ <*> f "column-limit" <*> f "function-arrows" <*> f "comma-style"+ <*> f "record-style" <*> f "import-export-style" <*> f "import-grouping" <*> f "indent-wheres"@@ -348,6 +359,11 @@ | Trailing deriving (Eq, Show, Enum, Bounded) +data RecordStyle+ = RecordStyleAligned+ | RecordStyleKnr+ deriving (Eq, Show, Enum, Bounded)+ data FunctionArrowsStyle = TrailingArrows | LeadingArrows@@ -450,6 +466,28 @@ Leading -> "leading" Trailing -> "trailing" +instance Aeson.FromJSON RecordStyle where+ parseJSON =+ Aeson.withText "RecordStyle" $ \s ->+ either Aeson.parseFail pure $+ parsePrinterOptType (Text.unpack s)++instance PrinterOptsFieldType RecordStyle where+ parsePrinterOptType s =+ case s of+ "aligned" -> Right RecordStyleAligned+ "knr" -> Right RecordStyleKnr+ _ ->+ Left . unlines $+ [ "unknown value: " <> show s+ , "Valid values are: \"aligned\" or \"knr\""+ ]++instance RenderPrinterOpt RecordStyle where+ renderPrinterOpt = \case+ RecordStyleAligned -> "aligned"+ RecordStyleKnr -> "knr"+ instance Aeson.FromJSON FunctionArrowsStyle where parseJSON = Aeson.withText "FunctionArrowsStyle" $ \s ->@@ -776,6 +814,9 @@ , "" , "# How to place commas in multi-line lists, records, etc. (choices: leading or trailing)" , "comma-style: leading"+ , ""+ , "# How to place braces in records (choices: aligned or knr)"+ , "record-style: aligned" , "" , "# Styling of import/export lists (choices: leading, trailing, or diff-friendly)" , "import-export-style: diff-friendly"
src/Ormolu/Config/Types.hs view
@@ -1,20 +1,22 @@+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Ormolu.Config.Types ( ImportGroup (..), ImportGroupRule (..),- ImportModuleMatcher (..), ImportRulePriority (..), matchAllRulePriority, matchLocalRulePriority, defaultImportRulePriority,+ ImportListMatcher (..), QualifiedImportMatcher (..),+ ImportScopeMatcher (..), ) where -import Control.Applicative (Alternative (..), asum)-import Data.Aeson ((.!=), (.:), (.:?))+import Control.Applicative (Alternative (..))+import Data.Aeson ((.!=), (.:?)) import Data.Aeson qualified as Aeson import Data.Aeson.Types qualified as Aeson import Data.List.NonEmpty (NonEmpty)@@ -34,28 +36,47 @@ <*> Aeson.parseField o "rules" data ImportGroupRule = ImportGroupRule- { igrModuleMatcher :: !ImportModuleMatcher,+ { igrGlob :: !Glob,+ igrImportListMatcher :: !ImportListMatcher, igrQualifiedMatcher :: !QualifiedImportMatcher,+ igrScopeMatcher :: !ImportScopeMatcher, igrPriority :: !ImportRulePriority } deriving (Eq, Show) instance Aeson.FromJSON ImportGroupRule where parseJSON = Aeson.withObject "rule" $ \o -> do- let parseModuleMatcher = Aeson.parseJSON (Aeson.Object o)- failUnknownModuleMatcher = Aeson.parseFail "Unknown or invalid module matcher"- attemptParseModuleMatcher = parseModuleMatcher <|> failUnknownModuleMatcher- igrModuleMatcher <- attemptParseModuleMatcher+ legacyMatch <- o .:? "match"+ (defaultPriority, defaultScope) <- case legacyMatch of+ Just "all" -> pure (matchAllRulePriority, pure MatchAllModules)+ Just "local-modules" -> pure (matchLocalRulePriority, pure MatchLocalModules)+ Just other -> Aeson.parseFail $ "Unknown legacy match value: " <> other+ Nothing -> pure (defaultImportRulePriority, empty) - qualified <- o .:? "qualified"- igrQualifiedMatcher <- case qualified of- Just True -> pure MatchQualifiedOnly- Just False -> pure MatchUnqualifiedOnly- Nothing -> pure MatchBothQualifiedAndUnqualified+ igrGlob <- mkGlob <$> o .:? "glob" .!= "**" - let defaultPriority- | MatchAllModules <- igrModuleMatcher = matchAllRulePriority- | otherwise = defaultImportRulePriority+ igrImportListMatcher <-+ o .:? "import-list" .!= "any" >>= \case+ "any" -> pure MatchAnyImportDeclaration+ "explicit" -> pure MatchExplicitImportList+ "hiding" -> pure MatchHidingImportClause+ "none" -> pure MatchWholeModuleImport+ other -> Aeson.parseFail $ "Unknown import list matcher: " <> other++ igrQualifiedMatcher <-+ o .:? "qualified" >>= \case+ Just True -> pure MatchQualifiedOnly+ Just False -> pure MatchUnqualifiedOnly+ Nothing -> pure MatchBothQualifiedAndUnqualified++ igrScopeMatcher <-+ o .:? "scope" >>= \case+ Just "any" -> pure MatchAllModules+ Just "local" -> pure MatchLocalModules+ Just "external" -> pure MatchExternalModules+ Nothing -> defaultScope <|> pure MatchAllModules+ Just other -> Aeson.parseFail ("Unknown scope matcher: " <> other)+ igrPriority <- o .:? "priority" .!= defaultPriority pure ImportGroupRule {..}@@ -75,35 +96,21 @@ defaultImportRulePriority :: ImportRulePriority defaultImportRulePriority = ImportRulePriority 50 +data ImportListMatcher+ = MatchExplicitImportList+ | MatchHidingImportClause+ | MatchWholeModuleImport+ | MatchAnyImportDeclaration+ deriving (Eq, Show)+ data QualifiedImportMatcher = MatchQualifiedOnly | MatchUnqualifiedOnly | MatchBothQualifiedAndUnqualified deriving (Eq, Show) -data ImportModuleMatcher- = MatchAllModules+data ImportScopeMatcher+ = MatchExternalModules | MatchLocalModules- | MatchGlob !Glob+ | MatchAllModules deriving (Eq, Show)--instance Aeson.FromJSON ImportModuleMatcher where- parseJSON v =- asum- [ parseMatchModuleMatcher v,- parseGlobModuleMatcher v- ]- where- parseMatchModuleMatcher :: Aeson.Value -> Aeson.Parser ImportModuleMatcher- parseMatchModuleMatcher = Aeson.withObject "ImportModuleMatcher" $ \o -> do- c <- Aeson.parseField @String o "match"- case c of- "all" -> pure MatchAllModules- "local-modules" -> pure MatchLocalModules- other -> Aeson.parseFail $ "Unknown matcher: " <> other-- parseGlobModuleMatcher :: Aeson.Value -> Aeson.Parser ImportModuleMatcher- parseGlobModuleMatcher = Aeson.withObject "ImportModuleMatcher" $ \o -> do- rawGlob <- o .: "glob"- let glob = mkGlob rawGlob- pure (MatchGlob glob)
src/Ormolu/Diff/ParseResult.hs view
@@ -2,6 +2,8 @@ {-# LANGUAGE DeepSubsumption #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ViewPatterns #-} @@ -14,6 +16,7 @@ import Data.ByteString (ByteString) import Data.Char (isSpace)+import Data.Choice (pattern Without) import Data.Foldable import Data.Function import Data.Generics@@ -62,8 +65,16 @@ } = diffCommentStream cstream0 cstream1 <> diffHsModule- hs0 {hsmodImports = concat . normalizeImports False mempty ImportGroupSingle $ hsmodImports hs0}- hs1 {hsmodImports = concat . normalizeImports False mempty ImportGroupSingle $ hsmodImports hs1}+ hs0 {hsmodImports = concat . normalizeImports' $ hsmodImports hs0}+ hs1 {hsmodImports = concat . normalizeImports' $ hsmodImports hs1}+ where+ -- The exact parameters here don't matter, it just needs to be consistent+ normalizeImports' =+ normalizeImports+ (Without #implicitPrelude)+ False+ mempty+ ImportGroupSingle diffCommentStream :: CommentStream -> CommentStream -> ParseResultDiff diffCommentStream (CommentStream cs) (CommentStream cs')@@ -106,11 +117,13 @@ `extQ` considerEqual @EpLayout `extQ` considerEqual @AnnSig `extQ` considerEqual @HsRuleAnn- `extQ` considerEqual @EpLinearArrow+ `extQ` considerEqual @EpLinear `extQ` considerEqual @AnnSynDecl `extQ` considerEqual @IsUnicodeSyntax -- FastString (for example for string literals) `extQ` considerEqualVia' ((==) @FastString)+ -- ModuleName is a newtype of FastString+ `extQ` considerEqualVia' ((==) @ModuleName) -- Haddock strings `extQ` hsDocStringEq -- Whether imports are pre- or post-qualified@@ -170,6 +183,7 @@ epAnnEq :: EpAnn a -> b -> ParseResultDiff epAnnEq _ _ = Same + importDeclQualifiedStyleEq :: forall a. (Data a) => ImportDeclQualifiedStyle -> a -> ParseResultDiff importDeclQualifiedStyleEq = considerEqualVia' f where f QualifiedPre QualifiedPost = True@@ -177,7 +191,7 @@ f x x' = x == x' hsDocStringEq :: HsDocString -> GenericQ ParseResultDiff- hsDocStringEq = considerEqualVia' ((==) `on` (map (T.dropWhile isSpace) . splitDocString True))+ hsDocStringEq = considerEqualVia' ((==) `on` (map (T.dropWhile isSpace) . splitDocString)) forLocated :: (Data e0, Data e1) =>
src/Ormolu/Diff/Text.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QualifiedDo #-}@@ -24,9 +23,6 @@ import GHC.Types.SrcLoc import Ormolu.Terminal import Ormolu.Terminal.QualifiedDo qualified as Term-#if !MIN_VERSION_base(4,20,0)-import Data.List (foldl')-#endif ---------------------------------------------------------------------------- -- Types
src/Ormolu/Fixity/Internal.hs view
@@ -190,7 +190,7 @@ defaultFixityOverrides :: FixityOverrides defaultFixityOverrides = FixityOverrides Map.empty --- | Module re-exports+-- | Module re-exports. newtype ModuleReexports = ModuleReexports { unModuleReexports :: Map ModuleName (NonEmpty (Maybe PackageName, ModuleName)) }@@ -198,61 +198,7 @@ -- | Module re-exports to apply by default. defaultModuleReexports :: ModuleReexports-defaultModuleReexports =- ModuleReexports . Map.fromList $- [ ( "Control.Lens",- l- "lens"- [ "Control.Lens.At",- "Control.Lens.Cons",- "Control.Lens.Each",- "Control.Lens.Empty",- "Control.Lens.Equality",- "Control.Lens.Fold",- "Control.Lens.Getter",- "Control.Lens.Indexed",- "Control.Lens.Iso",- "Control.Lens.Lens",- "Control.Lens.Level",- "Control.Lens.Plated",- "Control.Lens.Prism",- "Control.Lens.Reified",- "Control.Lens.Review",- "Control.Lens.Setter",- "Control.Lens.TH",- "Control.Lens.Traversal",- "Control.Lens.Tuple",- "Control.Lens.Type",- "Control.Lens.Wrapped",- "Control.Lens.Zoom"- ]- ),- ( "Servant",- l- "servant"- [ "Servant.API"- ]- ),- ( "Optics",- l- "optics"- [ "Optics.Fold",- "Optics.Operators",- "Optics.IxAffineFold",- "Optics.IxFold",- "Optics.IxTraversal",- "Optics.Traversal"- ]- ),- ( "Test.Hspec",- l- "hspec-expectations"- [ "Test.Hspec.Expectations"- ]- )- ]- where- l packageName xs = (Just packageName,) <$> NE.fromList xs+defaultModuleReexports = ModuleReexports Map.empty -- | Fixity information that is specific to a package being formatted. It -- requires module-specific imports in order to be usable.
src/Ormolu/Imports.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-}@@ -13,6 +13,8 @@ import Data.Bifunctor import Data.Char (isAlphaNum)+import Data.Choice (Choice)+import Data.Choice qualified as Choice import Data.Function (on) import Data.List (nubBy, sortBy, sortOn) import Data.Map.Strict (Map)@@ -28,30 +30,41 @@ import GHC.Types.SourceText import GHC.Types.SrcLoc import Ormolu.Config (ImportGrouping)-import Ormolu.Imports.Grouping (Import (..), groupImports, prepareExistingGroups)+import Ormolu.Imports.Grouping (Import (..), ImportList (..), groupImports, prepareExistingGroups) import Ormolu.Utils (notImplemented, showOutputable)-#if !MIN_VERSION_base(4,20,0)-import Data.List (foldl')-#endif -- | Sort, group and normalize imports. -- -- Assumes input list is sorted by source location. Output list is not necessarily -- sorted by source location, so this function should be called at most once on a -- given input list.-normalizeImports :: Bool -> Set Cabal.ModuleName -> ImportGrouping -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]]-normalizeImports respectful localModules importGrouping =+normalizeImports ::+ Choice "implicitPrelude" ->+ Bool ->+ Set Cabal.ModuleName ->+ ImportGrouping ->+ [LImportDecl GhcPs] ->+ [[LImportDecl GhcPs]]+normalizeImports implicitPrelude respectful localModules importGrouping = map (fmap snd) . concatMap ( groupImports importGrouping localModules toImport . M.toAscList . M.fromListWith combineImports- . fmap (\x -> (importId x, g x))+ . fmap (\x -> (importId implicitPrelude x, g x)) ) . prepareExistingGroups importGrouping respectful where toImport :: (ImportId, x) -> Import- toImport (ImportId {..}, _) = Import {importName = importIdName, importQualified}+ toImport (ImportId {..}, _) =+ Import+ { importName = importIdName,+ importList = case importHiding of+ Just (ImportListInterpretationOrd Exactly) -> Just ImportList+ Just (ImportListInterpretationOrd EverythingBut) -> Just HidingList+ Nothing -> Nothing,+ importQualified+ } g :: LImportDecl GhcPs -> LImportDecl GhcPs g (L l ImportDecl {..}) =@@ -90,10 +103,23 @@ importSafe :: Bool, importQualified :: Bool, importAs :: Maybe ModuleName,- importHiding :: Maybe ImportListInterpretationOrd+ importHiding :: Maybe ImportListInterpretationOrd,+ importLevel :: Maybe ImportDeclLevelOrd } deriving (Eq, Ord) +-- | A wrapper for 'ImportDeclLevel' that provides an 'Ord' instance.+newtype ImportDeclLevelOrd = ImportDeclLevelOrd+ { unImportDeclLevelOrd :: ImportDeclLevel+ }+ deriving stock (Eq)++instance Ord ImportDeclLevelOrd where+ compare = compare `on` toBool . unImportDeclLevelOrd+ where+ toBool ImportDeclSplice = False+ toBool ImportDeclQuote = True+ data ImportPkgQual = -- | The import is not qualified by a package name. NoImportPkgQual@@ -124,8 +150,8 @@ toBool EverythingBut = True -- | Obtain an 'ImportId' for a given import.-importId :: LImportDecl GhcPs -> ImportId-importId (L _ ImportDecl {..}) =+importId :: Choice "implicitPrelude" -> LImportDecl GhcPs -> ImportId+importId implicitPrelude (L _ ImportDecl {..}) = ImportId { importIsPrelude = isPrelude, importIdName = moduleName,@@ -137,11 +163,17 @@ QualifiedPost -> True NotQualified -> False, importAs = unLoc <$> ideclAs,- importHiding = ImportListInterpretationOrd . fst <$> ideclImportList+ importHiding = ImportListInterpretationOrd . fst <$> ideclImportList,+ importLevel = importLevelOf ideclLevelSpec } where- isPrelude = moduleNameString moduleName == "Prelude"+ isPrelude =+ Choice.isTrue implicitPrelude && moduleNameString moduleName == "Prelude" moduleName = unLoc ideclName+ importLevelOf = \case+ LevelStylePre l -> Just (ImportDeclLevelOrd l)+ LevelStylePost l -> Just (ImportDeclLevelOrd l)+ NotLevelled -> Nothing -- | Normalize a collection of import items. normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]@@ -231,6 +263,7 @@ IEDefault _ x -> (1, x) IEPattern _ x -> (2, x) IEType _ x -> (3, x)+ IEData _ x -> (4, x) compareRdrName :: RdrName -> RdrName -> Ordering compareRdrName x y =
src/Ormolu/Imports/Grouping.hs view
@@ -3,6 +3,7 @@ module Ormolu.Imports.Grouping ( Import (..),+ ImportList (..), prepareExistingGroups, groupImports, )@@ -19,18 +20,24 @@ import Distribution.ModuleName qualified as Cabal import GHC.Hs (GhcPs, getLocA) import Language.Haskell.Syntax (LImportDecl, ModuleName, moduleNameString)-import Ormolu.Config (ImportGroup (..), ImportGroupRule (..), ImportGrouping (..), ImportModuleMatcher (..))+import Ormolu.Config (ImportGroup (..), ImportGroupRule (..), ImportGrouping (..)) import Ormolu.Config qualified as Config import Ormolu.Utils (ghcModuleNameToCabal, groupBy', separatedByBlank)-import Ormolu.Utils.Glob (matchesGlob)+import Ormolu.Utils.Glob (matchAllGlob, matchesGlob) newtype ImportGroups = ImportGroups (NonEmpty ImportGroup) data Import = Import { importName :: ModuleName,+ importList :: Maybe ImportList, importQualified :: Bool } +data ImportList+ = ImportList+ | HidingList+ deriving (Eq)+ importGroupSingleStrategy :: ImportGroups importGroupSingleStrategy = ImportGroups $@@ -94,16 +101,20 @@ matchAllImportRule :: ImportGroupRule matchAllImportRule = ImportGroupRule- { igrModuleMatcher = MatchAllModules,+ { igrGlob = matchAllGlob,+ igrImportListMatcher = Config.MatchAnyImportDeclaration, igrQualifiedMatcher = Config.MatchBothQualifiedAndUnqualified,+ igrScopeMatcher = Config.MatchAllModules, igrPriority = Config.matchAllRulePriority } matchLocalModulesRule :: ImportGroupRule matchLocalModulesRule = ImportGroupRule- { igrModuleMatcher = MatchLocalModules,+ { igrGlob = matchAllGlob,+ igrImportListMatcher = Config.MatchAnyImportDeclaration, igrQualifiedMatcher = Config.MatchBothQualifiedAndUnqualified,+ igrScopeMatcher = Config.MatchLocalModules, igrPriority = Config.matchLocalRulePriority } @@ -122,16 +133,30 @@ } matchesRule :: Set Cabal.ModuleName -> Import -> ImportGroupRule -> Bool-matchesRule localMods Import {..} ImportGroupRule {..} = matchesModules && matchesQualified+matchesRule localMods Import {..} ImportGroupRule {..} =+ and+ [ matchingGlob,+ matchingImportList,+ matchingQualified,+ matchingScope+ ] where- matchesModules = case igrModuleMatcher of- MatchAllModules -> True- MatchLocalModules -> ghcModuleNameToCabal importName `Set.member` localMods- MatchGlob gl -> moduleNameString importName `matchesGlob` gl- matchesQualified = case igrQualifiedMatcher of+ matchingGlob = moduleNameString importName `matchesGlob` igrGlob+ matchingImportList = case igrImportListMatcher of+ Config.MatchExplicitImportList -> importList == Just ImportList+ Config.MatchHidingImportClause -> importList == Just HidingList+ Config.MatchWholeModuleImport -> importList == Nothing+ Config.MatchAnyImportDeclaration -> True+ matchingQualified = case igrQualifiedMatcher of Config.MatchQualifiedOnly -> importQualified Config.MatchUnqualifiedOnly -> not importQualified Config.MatchBothQualifiedAndUnqualified -> True+ matchingScope =+ let isLocalModule = ghcModuleNameToCabal importName `Set.member` localMods+ in case igrScopeMatcher of+ Config.MatchAllModules -> True+ Config.MatchExternalModules -> not isLocalModule+ Config.MatchLocalModules -> isLocalModule prepareExistingGroups :: ImportGrouping -> Bool -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]] prepareExistingGroups ig respectful =
src/Ormolu/Parser.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}@@ -18,6 +19,8 @@ import Control.Monad.Except (ExceptT (..), runExceptT) import Control.Monad.IO.Class import Data.Char (isSpace)+import Data.Choice (Choice)+import Data.Choice qualified as Choice import Data.Functor import Data.Functor.Identity (Identity (..)) import Data.Generics hiding (orElse)@@ -30,7 +33,7 @@ import GHC.Data.FastString qualified as GHC import GHC.Data.Maybe (orElse) import GHC.Data.StringBuffer (StringBuffer)-import GHC.Driver.Config.Parser (initParserOpts)+import GHC.Driver.Config.Parser (initParserOpts, supportedLanguagePragmas) import GHC.Driver.Errors.Types qualified as GHC import GHC.Driver.Session as GHC import GHC.DynFlags (baseDynFlags)@@ -45,6 +48,7 @@ import GHC.Types.SrcLoc import GHC.Utils.Error import GHC.Utils.Exception (ExceptionMonad)+import GHC.Utils.Logger (initLogger) import GHC.Utils.Panic qualified as GHC import Ormolu.Config import Ormolu.Config.Gen (SingleConstraintParens (..))@@ -91,8 +95,12 @@ parsePragmasIntoDynFlags baseFlags extraOpts path rawInputStringBuffer >>= \case Right res -> pure res Left err -> throwIO (OrmoluParsingFailed beginningLoc err)- let cppEnabled = EnumSet.member Cpp (GHC.extensionFlags dynFlags)- implicitPrelude = EnumSet.member ImplicitPrelude (GHC.extensionFlags dynFlags)+ let cppEnabled =+ Choice.fromBool $+ EnumSet.member Cpp (GHC.extensionFlags dynFlags)+ implicitPrelude =+ Choice.fromBool $+ EnumSet.member ImplicitPrelude (GHC.extensionFlags dynFlags) fixityImports <- parseImports dynFlags implicitPrelude path rawInputStringBuffer >>= \case Right res ->@@ -175,7 +183,10 @@ -- | Normalize a 'HsModule' by sorting its export lists, dropping -- blank comments, etc.-normalizeModule :: Config RegionDeltas -> HsModule GhcPs -> HsModule GhcPs+normalizeModule ::+ Config RegionDeltas ->+ HsModule GhcPs ->+ HsModule GhcPs normalizeModule Config {..} hsmod = everywhere ( mkT dropBlankTypeHaddocks@@ -317,10 +328,14 @@ let (_warnings, fileOpts) = GHC.getOptions (initParserOpts flags)+ (supportedLanguagePragmas flags) input filepath+ -- 'initLogger' does not have any hooks installed, so we don't get any+ -- (unwanted) output.+ logger <- initLogger (flags', leftovers, warnings) <-- parseDynamicFilePragma flags (extraOpts <> fileOpts)+ parseDynamicFilePragma logger flags (extraOpts <> fileOpts) case NE.nonEmpty leftovers of Nothing -> return () Just unrecognizedOpts ->@@ -332,8 +347,8 @@ parseImports :: -- | Pre-set 'DynFlags' DynFlags ->- -- | Implicit Prelude?- Bool ->+ -- | Whether the implicit Prelude is in effect+ Choice "implicitPrelude" -> -- | File name (only for source location annotations) FilePath -> -- | Input for the parser@@ -355,7 +370,11 @@ mod' = mmoduleName `orElse` L (GHC.noAnnSrcSpan main_loc) mAIN_NAME explicitImports = hsmodImports hsmod implicitImports =- GHC.mkPrelImports (unLoc mod') main_loc implicitPrelude explicitImports+ GHC.mkPrelImports+ (unLoc mod')+ main_loc+ (Choice.toBool implicitPrelude)+ explicitImports in Right (explicitImports ++ implicitImports) where popts = initParserOpts flags
src/Ormolu/Printer.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-} @@ -8,6 +9,7 @@ ) where +import Data.Choice (Choice) import Data.Text (Text) import Data.Text qualified as T import Ormolu.Config@@ -20,7 +22,7 @@ -- | Render several source snippets. printSnippets :: -- | Whether to print out debug information during printing- Bool ->+ Choice "debug" -> -- | Result of parsing [SourceSnippet] -> PrinterOptsTotal ->
src/Ormolu/Printer/Combinators.hs view
@@ -24,6 +24,7 @@ newline, declNewline, multilineCommentNewline,+ newlineLiteral, inci, inciBy, inciIf,@@ -37,6 +38,7 @@ switchLayout, switchLayoutNoLimit, spansLayout,+ enterLayout, Layout (..), vlayout, getLayout,@@ -48,6 +50,7 @@ -- ** Formatting lists sep, sepSemi,+ sepSemi', canUseBraces, useBraces, dontUseBraces,@@ -58,6 +61,7 @@ backticks, banana, braces,+ recordBraces, brackets, parens, parensHash,@@ -256,7 +260,25 @@ -- | Elements to render [a] -> R ()-sepSemi f xs = vlayout singleLine multiLine+sepSemi = sepSemi' False++-- | A version of 'sepSemi' that allows to control whether semicolons should+-- be inserted in multi-line layout.+--+-- > useBraces $ sepSemi' False txt ["foo", "bar"]+-- > == vlayout (txt "{ foo; bar }") (txt "foo\nbar")+--+-- > dontUseBraces $ sepSemi' True txt ["foo", "bar"]+-- > == vlayout (txt "foo; bar") (txt "foo;\nbar")+sepSemi' ::+ -- | Whether to insert semicolons in multi-line layout+ Bool ->+ -- | How to render an element+ (a -> R ()) ->+ -- | Elements to render+ [a] ->+ R ()+sepSemi' addMultiColSemi f xs = vlayout singleLine multiLine where singleLine = do ub <- canUseBraces@@ -272,7 +294,10 @@ txt "}" else sep (txt ";" >> space) f xs' multiLine =- sep newline (dontUseBraces . f) xs+ sep+ (if addMultiColSemi then txt ";" >> newline else newline)+ (dontUseBraces . f)+ xs ---------------------------------------------------------------------------- -- Wrapping@@ -368,6 +393,31 @@ else space >> sitcc m newline inciIf (style == S) close++-- With leading commas align the close brace with commas+-- otherwise move the close brace back to the left+recordBraces :: R () -> R ()+recordBraces m = do+ commaStyle <- getPrinterOpt poCommaStyle+ recordBraces_ (commaStyle == Trailing) m++recordBraces_ :: Bool -> R () -> R ()+recordBraces_ moveBraceBack m = do+ style <- getPrinterOpt poRecordStyle+ case style of+ RecordStyleAligned -> braces N m+ RecordStyleKnr ->+ vlayout+ (txt "{" >> m >> txt "}")+ ( do+ txt "{"+ newline+ sitcc m+ newline+ if moveBraceBack+ then inciByFrac (-1) (txt "}")+ else txt "}"+ ) ---------------------------------------------------------------------------- -- Literals
src/Ormolu/Printer/Internal.hs view
@@ -20,6 +20,7 @@ newline, declNewline, multilineCommentNewline,+ newlineLiteral, askSourceType, askModuleFixityMap, askDebug,@@ -70,7 +71,6 @@ import Data.Bool (bool) import Data.Char (isSpace) import Data.Choice (Choice)-import Data.Choice qualified as Choice import Data.Coerce import Data.Functor ((<&>)) import Data.Functor.Identity (runIdentity)@@ -122,7 +122,7 @@ -- | Module fixity map rcModuleFixityMap :: ModuleFixityMap, -- | Whether to print out debug information during printing- rcDebug :: !Bool+ rcDebug :: !(Choice "debug") } -- | State context of 'R'.@@ -194,8 +194,9 @@ EnumSet Extension -> -- | Module fixity map ModuleFixityMap ->+ -- | Whether to print out debug information during printing+ Choice "debug" -> -- | Resulting rendition- Bool -> Text runR (R m) sstream cstream printerOpts localModules sourceType extensions moduleFixityMap debug = TL.toStrict . toLazyText . scBuilder $ execState (runReaderT m rc) sc@@ -438,6 +439,19 @@ _ -> AfterNewline } +-- | Insert a newline literal without modifying the internal state of the+-- parser. This is to be used exceptionally, e.g. for printing multiline+-- string literals.+newlineLiteral :: R ()+newlineLiteral = R . modify $ \sc ->+ sc+ { scBuilder = scBuilder sc <> "\n",+ scColumn = 0,+ scIndent = 0,+ scThisLineSpans = [],+ scRequestedDelimiter = AfterNewline+ }+ -- | Return the source type. askSourceType :: R SourceType askSourceType = R (asks rcSourceType)@@ -449,7 +463,7 @@ -- | Retrieve whether we should print out certain debug information while -- printing. askDebug :: R (Choice "debug")-askDebug = R (asks (Choice.fromBool . rcDebug))+askDebug = R (asks rcDebug) -- | Like 'inci', but indents by exactly the given number of steps. inciBy :: Int -> R () -> R ()
src/Ormolu/Printer/Meat/Common.hs view
@@ -15,6 +15,7 @@ p_hsDoc', p_sourceText, p_namespaceSpec,+ p_hsMultAnn, p_arrow, ) where@@ -29,13 +30,13 @@ import GHC.Hs.Doc import GHC.Hs.Extension (GhcPs) import GHC.Hs.ImpExp+import GHC.Hs.Type import GHC.LanguageExtensions.Type (Extension (..)) import GHC.Parser.Annotation import GHC.Types.Name.Occurrence (OccName (..), occNameString) import GHC.Types.Name.Reader import GHC.Types.SourceText import GHC.Types.SrcLoc-import Language.Haskell.Syntax (HsArrowOf (..)) import Language.Haskell.Syntax.Module.Name import Ormolu.Config import Ormolu.Printer.Combinators@@ -48,7 +49,7 @@ | -- | Top-level declarations Free --- | Outputs the name of the module-like entity, preceeded by the correct prefix ("module" or "signature").+-- | Outputs the name of the module-like entity, preceded by the correct prefix ("module" or "signature"). p_hsmodName :: ModuleName -> R () p_hsmodName mname = do sourceType <- askSourceType@@ -73,6 +74,10 @@ txt "type" space p_rdrName x+ IEData _ x -> do+ txt "data"+ space+ p_rdrName x -- | Render a @'LocatedN' 'RdrName'@. p_rdrName :: LocatedN RdrName -> R ()@@ -93,7 +98,7 @@ -- insert spaces when we have a parenthesized operator starting with `#`. handleUnboxedSumsAndHashInteraction | unboxedSums,- -- Qualified names do not start wth a `#`.+ -- Qualified names do not start with a `#`. Unqual (occNameString -> '#' : _) <- x = \y -> space *> y <* space | otherwise = id@@ -126,16 +131,16 @@ -- | A helper for formatting infix constructions in lhs of definitions. p_infixDefHelper :: -- | Whether to format in infix style- Bool ->+ Choice "infixStyle" -> -- | Whether to bump indentation for arguments- Bool ->+ Choice "indentArgs" -> -- | How to print the operator\/name R () -> -- | How to print the arguments [R ()] -> R () p_infixDefHelper isInfix indentArgs name args =- case (isInfix, args) of+ case (Choice.toBool isInfix, args) of (True, p0 : p1 : ps) -> do let parens' = if null ps@@ -148,14 +153,15 @@ name space p1- unless (null ps) . inciIf indentArgs $ do+ unless (null ps) . inciIf (Choice.toBool indentArgs) $ do breakpoint sitcc (sep breakpoint sitcc ps) (_, ps) -> do name unless (null ps) $ do breakpoint- inciIf indentArgs $ sitcc (sep breakpoint sitcc args)+ inciIf (Choice.toBool indentArgs) $+ sitcc (sep breakpoint sitcc args) -- | Print a Haddock. p_hsDoc ::@@ -190,12 +196,7 @@ -- Make sure the Haddock is separated by a newline from other comments. when goesAfterComment newline - let shouldEscapeCommentBraces =- case poHStyle of- HaddockSingleLine -> False- HaddockMultiLine -> True- HaddockMultiLineCompact -> True- let docStringLines = splitDocString shouldEscapeCommentBraces $ hsDocString str+ let docStringLines = splitDocString $ hsDocString str if poHStyle == HaddockSingleLine || length docStringLines <= 1 then do@@ -242,10 +243,18 @@ TypeNamespaceSpecifier _ -> txt "type" *> space DataNamespaceSpecifier _ -> txt "data" *> space -p_arrow :: (mult -> R ()) -> HsArrowOf mult GhcPs -> R ()+p_hsMultAnn :: (mult -> R ()) -> HsMultAnnOf mult GhcPs -> R ()+p_hsMultAnn p_mult = \case+ HsUnannotated _ -> pure ()+ HsLinearAnn _ -> txt "%1"+ HsExplicitMult _ mult -> txt "%" *> p_mult mult++-- | Like 'p_hsMultAnn', except specifically for arrows, taking -XUnicodeSyntax+-- into account.+p_arrow :: (mult -> R ()) -> HsMultAnnOf mult GhcPs -> R () p_arrow p_mult = \case- HsUnrestrictedArrow _ -> token'rarrow- HsLinearArrow _ -> token'lolly+ HsUnannotated _ -> token'rarrow+ HsLinearAnn _ -> token'lolly HsExplicitMult _ mult -> do txt "%" p_mult mult
src/Ormolu/Printer/Meat/Declaration.hs view
@@ -256,7 +256,7 @@ TypeSynonym :: RdrName -> HsDecl GhcPs pattern InlinePragma n <- SigD _ (InlineSig _ (L _ n) _)-pattern SpecializePragma n <- SigD _ (SpecSig _ (L _ n) _ _)+pattern SpecializePragma n <- SigD _ (isSpecSig -> Just n) pattern SCCPragma n <- SigD _ (SCCFunSig _ (L _ n) _) pattern AnnTypePragma n <- AnnD _ (HsAnnotation _ (TypeAnnProvenance (L _ n)) _) pattern AnnValuePragma n <- AnnD _ (HsAnnotation _ (ValueAnnProvenance (L _ n)) _)@@ -266,6 +266,12 @@ pattern KindSignature n <- KindSigD _ (StandaloneKindSig _ (L _ n) _) pattern FamilyDeclaration n <- TyClD _ (FamDecl _ (FamilyDecl _ _ _ (L _ n) _ _ _ _)) pattern TypeSynonym n <- TyClD _ (SynDecl _ (L _ n) _ _ _)++isSpecSig :: Sig GhcPs -> Maybe RdrName+isSpecSig = \case+ SpecSig _ (L _ n) _ _ -> Just n+ SpecSigE _ _ (deconstructExprFromSpecSigE -> (L _ n, _, _)) _ -> Just n+ _ -> Nothing -- Declarations which can refer to multiple names
src/Ormolu/Printer/Meat/Declaration/Class.hs view
@@ -1,5 +1,8 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} -- | Rendering of type class declarations.@@ -10,6 +13,8 @@ import Control.Arrow import Control.Monad+import Data.Choice (pattern Is)+import Data.Choice qualified as Choice import Data.Foldable import Data.Function (on) import Data.List (sortBy)@@ -61,8 +66,8 @@ for_ ctx p_classContext switchLayout signatureSpans $ p_infixDefHelper- (isInfix fixity)- True+ (Choice.fromBool (isInfix fixity))+ (Is #indentArgs) (p_rdrName name) (located' p_hsTyVarBndr <$> hsq_explicit) inci (p_classFundeps fdeps)
src/Ormolu/Printer/Meat/Declaration/Data.hs view
@@ -3,9 +3,12 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_GHC -Wno-orphans #-} -- | Renedring of data type declarations. module Ormolu.Printer.Meat.Declaration.Data@@ -21,7 +24,6 @@ import Data.List.NonEmpty qualified as NE import Data.Maybe (isJust, isNothing, maybeToList) import Data.Text qualified as Text-import Data.Void import GHC.Hs import GHC.Types.Fixity import GHC.Types.ForeignCall@@ -79,8 +81,8 @@ forM_ dd_ctxt p_lhsContext switchLayout constructorSpans $ p_infixDefHelper- (isInfix fixity)- True+ (Choice.fromBool (isInfix fixity))+ (Is #indentArgs) (p_rdrName name) (p_tyVar <$> tyVars) forM_ dd_kindSig $ \k -> do@@ -154,7 +156,7 @@ deriving (Eq, Ord) p_conDecl :: Choice "singleRecCon" -> ConDecl GhcPs -> R ()-p_conDecl _ ConDeclGADT {..} = do+p_conDecl _ decl@ConDeclGADT {..} = do mapM_ (p_hsDoc Pipe (With #endNewline)) con_doc switchLayout conDeclSpn $ do let c :| cs = con_names@@ -162,61 +164,42 @@ unless (null cs) . inci $ do commaDel sep commaDel p_rdrName cs- inci $ do- let conTy = case con_g_args of- PrefixConGADT NoExtField xs ->- let go (HsScaled a b) t = addCLocA t b (HsFunTy NoExtField a b t)- in foldr go con_res_ty xs- RecConGADT _ r ->- addCLocA r con_res_ty $- HsFunTy- NoExtField- (HsUnrestrictedArrow noAnn)- (la2la $ HsRecTy noAnn <$> r)- con_res_ty- qualTy = case con_mb_cxt of- Nothing -> conTy- Just qs ->- addCLocA qs conTy $- HsQualTy NoExtField qs conTy- quantifiedTy :: LHsType GhcPs- quantifiedTy =- addCLocA con_bndrs qualTy $- hsOuterTyVarBndrsToHsType (unLoc con_bndrs) qualTy- p_hsTypeAnnotation quantifiedTy+ inci $ p_hsFun decl where conDeclSpn =- fmap getLocA (NE.toList con_names)- <> [getLocA con_bndrs]+ fmap getLocA (NE.toList con_names) <> conSigSpans+ conSigSpans =+ [getLocA con_outer_bndrs] <> maybeToList (fmap getLocA con_mb_cxt)- <> conArgsSpans- conArgsSpans = case con_g_args of- PrefixConGADT NoExtField xs -> getLocA . hsScaledThing <$> xs- RecConGADT _ x -> [getLocA x]+ <> conArgResSpans+ conArgResSpans =+ getLocA con_res_ty : case con_g_args of+ PrefixConGADT NoExtField xs -> getLocA . cdf_type <$> xs+ RecConGADT _ x -> [getLocA x] p_conDecl singleRecCon ConDeclH98 {..} = case con_args of- PrefixCon (_ :: [Void]) xs -> do+ PrefixCon xs -> do renderConDoc renderContext switchLayout conDeclSpn $ do p_rdrName con_name- let args = hsScaledThing <$> xs- argsHaveDocs = conArgsHaveHaddocks args+ let argsHaveDocs = conArgsHaveHaddocks xs delimiter = if argsHaveDocs then newline else breakpoint unless (null xs) delimiter inci . sitcc $- sep delimiter (sitcc . located' p_hsType) args+ sep delimiter (sitcc . p_hsConDeclFieldWithDoc) xs RecCon l -> do renderConDoc renderContext switchLayout conDeclSpn $ do p_rdrName con_name- breakpoint- inciIf (Choice.isFalse singleRecCon) (located l p_conDeclFields)- InfixCon (HsScaled _ l) (HsScaled _ r) -> do+ recordStyle <- getPrinterOpt poRecordStyle+ if recordStyle == RecordStyleKnr then space else breakpoint+ inciIf (Choice.isFalse singleRecCon) (located l p_hsConDeclRecFields)+ InfixCon l r -> do -- manually render these- let (lType, larg_doc) = splitDocTy l- let (rType, rarg_doc) = splitDocTy r+ let larg_doc = cdf_doc l+ rarg_doc = cdf_doc r -- the constructor haddock can go on top of the entire constructor -- only if neither argument has haddocks@@ -229,10 +212,10 @@ if isJust con_doc then do mapM_ (p_hsDoc Pipe (With #endNewline)) larg_doc- located lType p_hsType+ p_hsConDeclField l breakpoint else do- located lType p_hsType+ p_hsConDeclField l case larg_doc of Just doc -> space >> p_hsDoc Caret (With #endNewline) doc Nothing -> breakpoint@@ -242,7 +225,7 @@ case rarg_doc of Just doc -> newline >> p_hsDoc Pipe (With #endNewline) doc Nothing -> breakpoint- located rType p_hsType+ p_hsConDeclField r where renderConDoc = mapM_ (p_hsDoc Pipe (With #endNewline)) con_doc renderContext =@@ -262,13 +245,9 @@ conDeclSpn = conNameSpn : conArgsSpans conNameSpn = getLocA con_name conArgsSpans = case con_args of- PrefixCon (_ :: [Void]) xs -> getLocA . hsScaledThing <$> xs+ PrefixCon xs -> getLocA . cdf_type <$> xs RecCon l -> [getLocA l]- InfixCon x y -> getLocA . hsScaledThing <$> [x, y]-- splitDocTy = \case- L _ (HsDocTy _ ty doc) -> (ty, Just doc)- ty -> (ty, Nothing)+ InfixCon x y -> getLocA . cdf_type <$> [x, y] p_lhsContext :: LHsContext GhcPs ->@@ -337,6 +316,105 @@ located sigTy p_hsSigType ----------------------------------------------------------------------------+-- FunRepr ConDeclGADT++-- | ConDecl, except should only be called with the ConDeclGADT constructor.+type ConDeclGADT = ConDecl++-- | FunRepr ConDeclGADT renders a GADT constructor type annotation, which looks+-- similar to a function type, except the arguments of the function have two+-- additional capabilities:+-- * They can specify UNPACK/strictness+-- * It can be a single argument with a record fields syntax+instance FunRepr (ConDeclGADT GhcPs) where+ parseFunRepr = \case+ L _ ConDeclGADT {..} ->+ fst+ . addSig+ . addOuter con_outer_bndrs+ . addInner con_inner_bndrs+ . addCtx con_mb_cxt+ . addArgs con_g_args+ $ mkRet con_res_ty+ _ -> error "parseFunRepr @ConDeclGADT unexpectedly called on non-GADT constructor"+ where+ addSig (next, loc) = (ParsedFunSig {sig = (), next}, loc)+ addOuter (L ann bndrs) =+ case bndrs :: HsOuterSigTyVarBndrs GhcPs of+ HsOuterImplicit {} -> id+ HsOuterExplicit _ bndrs' -> \(next, loc) ->+ let loc' = combineSrcSpans loc (getHasLoc ann)+ fun =+ ParsedFunForall+ { tele = L (l2l loc') $ mkHsForAllInvisTele noAnn bndrs',+ next+ }+ in (fun, loc')+ addInner =+ let go tele (next, loc) =+ let loc' =+ combineSrcSpans loc $+ case tele of+ HsForAllVis x _ -> getHasLoc x+ HsForAllInvis x _ -> getHasLoc x+ fun = ParsedFunForall {tele = L (l2l loc') tele, next}+ in (fun, loc')+ in foldr (\tele acc -> go tele . acc) id+ addCtx = \case+ Nothing -> id+ Just ctxs -> \(next, loc) ->+ let loc' = combineSrcSpans loc (getHasLoc ctxs)+ fun = ParsedFunQuals {ctxs = [L (l2l loc') ctxs], next}+ in (fun, loc')+ addArgs details =+ case details :: HsConDeclGADTDetails GhcPs of+ PrefixConGADT _ fields ->+ let go field (next, loc) =+ let loc' = combineSrcSpans loc (getHasLoc field.cdf_type)+ fun =+ ParsedFunArg+ { span = l2l loc',+ arg = L noAnn (Left field),+ doc = field.cdf_doc,+ multAnn = field.cdf_multiplicity,+ next+ }+ in (fun, loc')+ in foldr (\field acc -> go field . acc) id fields+ RecConGADT _ fields -> \(next, loc) ->+ let loc' = combineSrcSpans (getHasLoc fields) loc+ fun =+ ParsedFunArg+ { span = l2l loc',+ arg = la2la $ Right <$> fields,+ doc = Nothing,+ multAnn = HsUnannotated (EpArrow noAnn),+ next+ }+ in (fun, loc')+ mkRet ty =+ let fun =+ case ty of+ L _ (HsDocTy _ ret doc) -> ParsedFunReturn {ret, doc = Just doc}+ ret -> ParsedFunReturn {ret, doc = Nothing}+ in (fun, getHasLoc ty)++ type FunReprCtx (ConDeclGADT GhcPs) = HsType GhcPs+ renderFunReprCtx = p_hsType++ -- Invariant: Exactly one of the following must be true:+ -- * There's exactly one 'Right' arg+ -- * There are zero or more 'Left' args+ type FunReprArg (ConDeclGADT GhcPs) = Either (HsConDeclField GhcPs) [LocatedA (HsConDeclRecField GhcPs)]+ renderFunReprArg = either p_hsConDeclField p_hsConDeclRecFields++ type FunReprMult (ConDeclGADT GhcPs) = HsType GhcPs+ renderFunReprMult = p_hsType++ type FunReprRet (ConDeclGADT GhcPs) = HsType GhcPs+ renderFunReprRet = p_hsType++---------------------------------------------------------------------------- -- Helpers isInfix :: LexicalFixity -> Bool@@ -349,14 +427,9 @@ where f ConDeclH98 {..} = isJust con_doc || case con_args of- PrefixCon [] xs ->- conArgsHaveHaddocks (hsScaledThing <$> xs)+ PrefixCon xs -> conArgsHaveHaddocks xs _ -> False f _ = False -conArgsHaveHaddocks :: [LBangType GhcPs] -> Bool-conArgsHaveHaddocks xs =- let hasDocs = \case- HsDocTy {} -> True- _ -> False- in any (hasDocs . unLoc) xs+conArgsHaveHaddocks :: [HsConDeclField GhcPs] -> Bool+conArgsHaveHaddocks = any (isJust . cdf_doc)
src/Ormolu/Printer/Meat/Declaration/Foreign.hs view
@@ -9,10 +9,12 @@ import Control.Monad import GHC.Hs import GHC.Types.ForeignCall+import GHC.Types.SourceText import GHC.Types.SrcLoc import Ormolu.Printer.Combinators import Ormolu.Printer.Meat.Common import Ormolu.Printer.Meat.Declaration.Signature+import Ormolu.Printer.Meat.Declaration.StringLiteral p_foreignDecl :: ForeignDecl GhcPs -> R () p_foreignDecl = \case@@ -56,8 +58,11 @@ located cCallConv atom -- Need to check for 'noLoc' for the 'safe' annotation when (isGoodSrcSpan $ getLocA safety) (space >> atom safety)- space- located sourceText p_sourceText+ inci $ located sourceText $ \case+ NoSourceText -> pure ()+ SourceText lit -> do+ breakpoint+ p_stringLit lit p_foreignExport :: ForeignExport GhcPs -> R () p_foreignExport (CExport sourceText (L loc (CExportStatic _ _ cCallConv))) = do
src/Ormolu/Printer/Meat/Declaration/OpTree.hs view
@@ -129,21 +129,34 @@ -- place the operator in a trailing position (because it would be -- read as being part of the do-block) && not (isDoBlock $ rightMostNode prevExpr)+ -- A staircase of two or more trailing operators is only worthwhile when+ -- the operand at the very end of the chain has a hanging form (a do+ -- block, lambda, case, etc.): the trailing operator then introduces that+ -- block. When such a chain ends in an ordinary expression (a variable,+ -- literal, or plain application) the trailing layout only produces+ -- ever-deepening indentation, so we fall back to the leading-operator+ -- layout. A single hard splitter is exempt: it does not form a pyramid+ -- and trailing is the idiomatic way to introduce its operand.+ chainEndsInHangingForm =+ case rightMostNode t of+ OpNode (L _ n) -> exprPlacement n == Hanging+ _ -> False+ isSingleOperator = case ops of+ [_] -> True+ _ -> False -- If all operators at the current level match the conditions to be- -- trailing, then put them in a trailing position- isTrailing = all couldBeTrailing $ zip (NE.toList exprs) ops+ -- trailing, and the chain is either a single operator or ends in a+ -- hanging form, then put the operators in a trailing position.+ isTrailing =+ (isSingleOperator || chainEndsInHangingForm)+ && all couldBeTrailing (zip (NE.toList exprs) ops) ub <- if isTrailing then return useBraces else opBranchBraceStyle placement indent <- getPrinterOpt poIndentation let p_x = ub $ p_exprOpTree s firstExpr putOpsExprs prevExpr (opi : ops') (expr : exprs') = do let isLast = null exprs' ub' = if not isLast then ub else id- -- Distinguish holes used in infix notation.- -- eg. '1 _foo 2' and '1 `_foo` 2'- opWrapper = case unLoc (opiOp opi) of- HsUnboundVar _ _ -> backticks- _ -> id- p_op = located (opiOp opi) (opWrapper . p_hsExpr)+ p_op = located (opiOp opi) p_hsExpr p_y = ub' $ p_exprOpTree N expr if isTrailing then do
src/Ormolu/Printer/Meat/Declaration/Rule.hs view
@@ -4,16 +4,16 @@ module Ormolu.Printer.Meat.Declaration.Rule ( p_ruleDecls,+ p_ruleBndrs, ) where -import Control.Monad (unless) import GHC.Hs import GHC.Types.Basic import GHC.Types.SourceText import Ormolu.Printer.Combinators import Ormolu.Printer.Meat.Common-import Ormolu.Printer.Meat.Declaration.Signature+import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.Signature import Ormolu.Printer.Meat.Declaration.Value import Ormolu.Printer.Meat.Type @@ -22,21 +22,12 @@ pragma "RULES" $ sep breakpoint (sitcc . located' p_ruleDecl) xs p_ruleDecl :: RuleDecl GhcPs -> R ()-p_ruleDecl (HsRule _ ruleName activation tyvars ruleBndrs lhs rhs) = do+p_ruleDecl (HsRule _ ruleName activation ruleBndrs lhs rhs) = do located ruleName p_ruleName space p_activation activation space- case tyvars of- Nothing -> return ()- Just xs -> do- p_forallBndrs ForAllInvis p_hsTyVarBndr xs- space- -- It appears that there is no way to tell if there was an empty forall- -- in the input or no forall at all. We do not want to add redundant- -- foralls, so let's just skip the empty ones.- unless (null ruleBndrs) $- p_forallBndrs ForAllInvis p_ruleBndr ruleBndrs+ p_ruleBndrs ruleBndrs breakpoint inci $ do located lhs p_hsExpr@@ -48,6 +39,17 @@ p_ruleName :: RuleName -> R () p_ruleName name = atom (HsString NoSourceText name :: HsLit GhcPs)++p_ruleBndrs :: RuleBndrs GhcPs -> R ()+p_ruleBndrs (RuleBndrs HsRuleBndrsAnn {..} tyvars ruleBndrs) = do+ case tyvars of+ Nothing -> return ()+ Just xs -> do+ p_forallBndrs ForAllInvis p_hsTyVarBndr xs+ space+ case rb_tmanns of+ Nothing -> pure ()+ Just _ -> p_forallBndrs ForAllInvis p_ruleBndr ruleBndrs p_ruleBndr :: RuleBndr GhcPs -> R () p_ruleBndr = \case
src/Ormolu/Printer/Meat/Declaration/Signature.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ViewPatterns #-} -- | Type signature declarations. module Ormolu.Printer.Meat.Declaration.Signature@@ -8,18 +9,24 @@ p_typeAscription, p_activation, p_standaloneKindSig,+ deconstructExprFromSpecSigE, ) where import Control.Monad+import Data.Maybe (maybeToList) import GHC.Data.BooleanFormula import GHC.Hs+import GHC.Stack (HasCallStack) import GHC.Types.Basic import GHC.Types.Fixity import GHC.Types.Name.Reader import GHC.Types.SourceText+import GHC.Types.SrcLoc import Ormolu.Printer.Combinators import Ormolu.Printer.Meat.Common+import Ormolu.Printer.Meat.Declaration.Rule+import Ormolu.Printer.Meat.Declaration.Value (p_hsExpr) import Ormolu.Printer.Meat.Type import Ormolu.Utils @@ -30,7 +37,9 @@ ClassOpSig _ def names sigType -> p_classOpSig def names sigType FixSig _ sig -> p_fixSig sig InlineSig _ name inlinePragma -> p_inlineSig name inlinePragma- SpecSig _ name ts inlinePragma -> p_specSig name ts inlinePragma+ SpecSig _ name ts inlinePragma ->+ p_specSig Nothing (noLocA $ HsVar NoExtField name) ts inlinePragma+ SpecSigE _ ruleBndrs expr inlinePragma -> p_specSigE ruleBndrs expr inlinePragma SpecInstSig _ sigType -> p_specInstSig sigType MinimalSig _ booleanFormula -> p_minimalSig booleanFormula CompleteMatchSig _ cs ty -> p_completeSig cs ty@@ -116,26 +125,71 @@ p_rdrName name p_specSig ::- -- | Name- LocatedN RdrName ->+ -- | Rule binders+ Maybe (RuleBndrs GhcPs) ->+ -- | Expression to specialize+ LHsExpr GhcPs -> -- | The types to specialize to [LHsSigType GhcPs] -> -- | For specialize inline InlinePragma -> R ()-p_specSig name ts InlinePragma {..} = pragmaBraces $ do+p_specSig mRuleBndrs specExpr ts InlinePragma {..} = pragmaBraces $ do txt "SPECIALIZE" space p_inlineSpec inl_inline space- p_activation inl_act- space- p_rdrName name- space- token'dcolon- breakpoint- inci $ sep commaDel (located' p_hsSigType) ts+ case (inl_inline, inl_act) of+ (NoInline _, NeverActive) -> return ()+ _ -> p_activation inl_act+ inci $ do+ space+ forM_ mRuleBndrs $ \ruleBndrs -> do+ p_ruleBndrs ruleBndrs+ space+ located specExpr p_hsExpr+ case ts of+ [] -> pure ()+ _ -> do+ space+ token'dcolon+ breakpoint+ sep commaDel (located' p_hsSigType) ts +p_specSigE ::+ -- | Rule binders+ RuleBndrs GhcPs ->+ -- | Expression to specialize+ LHsExpr GhcPs ->+ -- | For specialize inline+ InlinePragma ->+ R ()+p_specSigE ruleBndrs expr =+ p_specSig (Just ruleBndrs) specExpr (maybeToList sigTy)+ where+ (_, specExpr, sigTy) = deconstructExprFromSpecSigE expr++-- | The 'LHsExpr' in a 'SpecSigE' can only be of a very specific form, namely a+-- variable applied to value/type-level arguments, optionally with a type+-- signature.+--+-- https://github.com/ghc-proposals/ghc-proposals/blob/e2c683698323cec3e33625369ae2b5f585387c70/proposals/0493-specialise-expressions.rst#2proposed-change-specification+deconstructExprFromSpecSigE ::+ (HasCallStack) =>+ LHsExpr GhcPs ->+ (LocatedN RdrName, LHsExpr GhcPs, Maybe (LHsSigType GhcPs))+deconstructExprFromSpecSigE = \case+ L _ (ExprWithTySig _ e HsWC {hswc_body}) ->+ (findVar e, e, Just hswc_body)+ e -> (findVar e, e, Nothing)+ where+ findVar :: LHsExpr GhcPs -> LocatedN RdrName+ findVar = \case+ L _ (HsVar _ name) -> name+ L _ (HsApp _ e _) -> findVar e+ L _ (HsAppType _ e _) -> findVar e+ _ -> error "unreachble"+ p_inlineSpec :: InlineSpec -> R () p_inlineSpec = \case Inline _ -> txt "INLINE"@@ -165,7 +219,7 @@ p_minimalSig :: -- | Boolean formula- LBooleanFormula (LocatedN RdrName) ->+ LBooleanFormula GhcPs -> R () p_minimalSig = located' $ \booleanFormula ->@@ -173,7 +227,7 @@ p_booleanFormula :: -- | Boolean formula- BooleanFormula (LocatedN RdrName) ->+ BooleanFormula GhcPs -> R () p_booleanFormula = \case Var name -> p_rdrName name
+ src/Ormolu/Printer/Meat/Declaration/Signature.hs-boot view
@@ -0,0 +1,14 @@+module Ormolu.Printer.Meat.Declaration.Signature+ ( p_sigDecl,+ p_typeAscription,+ p_activation,+ )+where++import GHC.Hs+import GHC.Types.Basic+import Ormolu.Printer.Combinators++p_sigDecl :: Sig GhcPs -> R ()+p_typeAscription :: LHsSigType GhcPs -> R ()+p_activation :: Activation -> R ()
src/Ormolu/Printer/Meat/Declaration/StringLiteral.hs view
@@ -40,7 +40,7 @@ LastPos -> txt "\\" *> txt s vlayout singleLine multiLine MultilineStringLiteral ->- sep breakpoint' txt segments+ sep newlineLiteral txt segments txt endMarker -- | The start/end marker of the literal, whether it is a regular or a multiline@@ -91,12 +91,25 @@ where go [] = [s] go ((pre, suf) : bs) = case T.uncons suf of- Just ('\\', T.uncons -> Just (c, s'))- | is_space c,- let rest = T.drop 1 $ T.dropWhile (/= '\\') s' ->+ Just ('\\', suf')+ | (gap, T.uncons -> Just ('\\', rest)) <- T.span is_space suf',+ -- If there is a space after the backslash, this definitely is a+ -- string gap. Continue parsing gaps after the next backslash.+ not $ T.null gap -> pre : splitGaps rest- | otherwise -> go $ (if c == '\\' then drop 1 else id) bs+ | otherwise ->+ -- Check whether @suf@ starts with an escape sequence involving+ -- another backslash. If so, it can not be the start of a string+ -- gap, so we skip it.+ let skipNextBackslash =+ any (`T.isPrefixOf` suf') escapesWithAnotherBackslash+ in go $ (if skipNextBackslash then drop 1 else id) bs _ -> go bs++ -- All escape sequences (without the initial backslash) with another+ -- backslash. See+ -- https://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6+ escapesWithAnotherBackslash = ["\\", "^\\"] -- See the the MultilineStrings GHC proposal and 'lexMultilineString' from -- "GHC.Parser.String" for reference.
src/Ormolu/Printer/Meat/Declaration/Type.hs view
@@ -1,4 +1,7 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} -- | Rendering of type synonym declarations.@@ -7,6 +10,7 @@ ) where +import Data.Choice (pattern Is, pattern Isn't) import GHC.Hs.Extension import GHC.Hs.Type import GHC.Parser.Annotation@@ -32,8 +36,11 @@ space switchLayout (getLocA name : map getLocA hsq_explicit) $ p_infixDefHelper- (case fixity of Infix -> True; _ -> False)- True+ ( case fixity of+ Infix -> Is #infixStyle+ _ -> Isn't #infixStyle+ )+ (Is #indentArgs) (p_rdrName name) (map (located' p_hsTyVarBndr) hsq_explicit) inci $ do
src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs view
@@ -1,5 +1,8 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} -- | Rendering of data\/type families.@@ -10,6 +13,8 @@ where import Control.Monad+import Data.Choice (pattern Is)+import Data.Choice qualified as Choice import Data.Maybe (isNothing) import GHC.Hs import GHC.Types.Fixity@@ -33,8 +38,8 @@ breakpoint switchLayout headerSpns $ do p_infixDefHelper- (isInfix fdFixity)- True+ (Choice.fromBool (isInfix fdFixity))+ (Is #indentArgs) (p_rdrName fdLName) (located' p_hsTyVarBndr <$> hsq_explicit) let resultSig = p_familyResultSigL fdResultSig@@ -93,8 +98,8 @@ let famLhsSpn = getLocA feqn_tycon : fmap lhsTypeArgSrcSpan feqn_pats switchLayout famLhsSpn $ p_infixDefHelper- (isInfix feqn_fixity)- True+ (Choice.fromBool (isInfix feqn_fixity))+ (Is #indentArgs) (p_rdrName feqn_tycon) (p_lhsTypeArg <$> feqn_pats) inci $ do
src/Ormolu/Printer/Meat/Declaration/Value.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-}@@ -21,6 +22,7 @@ import Control.Monad import Data.Bool (bool)+import Data.Choice qualified as Choice import Data.Data hiding (Infix, Prefix) import Data.Function (on) import Data.Functor ((<&>))@@ -31,7 +33,6 @@ import Data.Maybe import Data.Text (Text) import Data.Text qualified as Text-import Data.Void import GHC.Data.Strict qualified as Strict import GHC.Hs import GHC.LanguageExtensions.Type (Extension (NegativeLiterals))@@ -47,7 +48,7 @@ import Ormolu.Printer.Meat.Common import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.OpTree-import Ormolu.Printer.Meat.Declaration.Signature+import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.Signature import Ormolu.Printer.Meat.Declaration.StringLiteral import Ormolu.Printer.Meat.Type import Ormolu.Printer.Operators@@ -100,15 +101,15 @@ MatchGroup GhcPs (LocatedA body) -> R () p_matchGroup' placer render style mg@MG {..} = do- let ob = case style of- Case -> bracesIfEmpty- LambdaCase -> bracesIfEmpty- _ -> dontUseBraces- where- bracesIfEmpty = if isEmptyMatchGroup mg then useBraces else id -- Since we are forcing braces on 'sepSemi' based on 'ob', we have to -- restore the brace state inside the sepsemi. ub <- bool dontUseBraces useBraces <$> canUseBraces+ let ob = case style of+ Case -> bracesIfNecessary+ LambdaCase -> bracesIfNecessary+ _ -> dontUseBraces+ where+ bracesIfNecessary = if isEmptyMatchGroup mg then useBraces else ub ob $ sepSemi (located' (ub . p_Match)) (unLoc mg_alts) where p_Match m@Match {..} =@@ -117,7 +118,7 @@ render (adjustMatchGroupStyle m style) (isInfixMatch m)- (HsNoMultAnn NoExtField)+ (HsUnannotated EpPatBind) (matchStrictness m) -- We use the spans of the individual patterns. (unLoc m_pats)@@ -126,7 +127,7 @@ -- | Function id obtained through pattern matching on 'FunBind' should not -- be used to print the actual equations because the different ‘RdrNames’ -- used in the equations may have different “decorations” (such as backticks--- and paretheses) associated with them. It is necessary to use per-equation+-- and parentheses) associated with them. It is necessary to use per-equation -- names obtained from 'm_ctxt' of 'Match'. This function replaces function -- name inside of 'Function' accordingly. adjustMatchGroupStyle ::@@ -134,14 +135,13 @@ MatchGroupStyle -> MatchGroupStyle adjustMatchGroupStyle m = \case- Function _ -> (Function . mc_fun . m_ctxt) m+ Function _ | FunRhs {mc_fun = f} <- m_ctxt m -> Function f style -> style matchStrictness :: Match id body -> SrcStrictness-matchStrictness match =- case m_ctxt match of- FunRhs {mc_strictness = s} -> s- _ -> NoSrcStrict+matchStrictness = \case+ Match {m_ctxt = FunRhs {mc_strictness = s}} -> s+ _ -> NoSrcStrict p_match :: -- | Style of the group@@ -186,17 +186,19 @@ -- would start with two indentation steps applied, which is ugly, so we -- need to be a bit more clever here and bump indentation level only when -- pattern group is multiline.+ p_hsMultAnn (located' p_hsType) multAnn case multAnn of- HsNoMultAnn NoExtField -> pure ()- HsPct1Ann _ -> txt "%1" *> space- HsMultAnn _ ty -> do- txt "%"- located ty p_hsType- space+ HsUnannotated {} -> pure ()+ HsLinearAnn {} -> space+ HsExplicitMult {} -> space case strictness of NoSrcStrict -> return () SrcStrict -> txt "!" SrcLazy -> txt "~"+ let isCase = \case+ Case -> True+ LambdaCase -> True+ _ -> False indentBody <- case NE.nonEmpty m_pats of Nothing -> False <$ case style of@@ -207,14 +209,19 @@ Function name -> combineSrcSpans (getLocA name) patSpans _ -> patSpans patSpans = combineSrcSpans' (getLocA <$> ne_pats)- indentBody = not (isOneLineSpan combinedSpans)+ containsOrPat = everything (||) $ \b -> case cast @_ @(Pat GhcPs) b of+ Just OrPat {} -> True+ _ -> False+ indentBody =+ not (isOneLineSpan combinedSpans)+ && not (isCase style && containsOrPat ne_pats) switchLayoutNoLimit [combinedSpans] $ do let stdCase = sep breakpoint (located' p_pat) m_pats case style of Function name -> p_infixDefHelper- isInfix- indentBody+ (Choice.fromBool isInfix)+ (Choice.fromBool indentBody) (p_rdrName name) (located' p_pat <$> m_pats) PatternBind -> stdCase@@ -244,14 +251,10 @@ Function name -> Just (getLocA name) _ -> Nothing Just pats -> (Just . getLocA . NE.last) pats- isCase = \case- Case -> True- LambdaCase -> True- _ -> False hasGuards = withGuards grhssGRHSs grhssSpan = combineSrcSpans' $- getGRHSSpan . unLoc <$> NE.fromList grhssGRHSs+ getGRHSSpan . unLoc <$> grhssGRHSs patGrhssSpan = maybe grhssSpan@@ -277,12 +280,19 @@ sep breakpoint (located' (p_grhs' placement placer render groupStyle))- grhssGRHSs+ (NE.toList grhssGRHSs)+ localBindsWhereSpan = case grhssLocalBinds of+ HsValBinds (EpAnn {anns = AnnList {al_rest}}) _ ->+ locA al_rest+ HsIPBinds (EpAnn {anns = AnnList {al_rest}}) _ ->+ locA al_rest+ EmptyLocalBinds _ -> noSrcSpan p_where = do unless (eqEmptyLocalBinds grhssLocalBinds) $ do breakpoint indentWhere <- getPrinterOpt poIndentWheres- bool (inciByFrac (-1 / 2)) id indentWhere $ txt "where"+ bool (inciByFrac (-1 / 2)) id indentWhere $ do+ located (L localBindsWhereSpan ()) $ \_ -> txt "where" breakpoint inciIf indentWhere $ p_hsLocalBinds grhssLocalBinds inciIf indentBody $ do@@ -290,6 +300,7 @@ case style of Function _ | hasGuards -> return () Function _ -> space >> inci equals+ PatternBind | hasGuards -> return () PatternBind -> space >> inci equals s | isCase s && hasGuards -> return () _ -> space >> token'rarrow@@ -375,10 +386,10 @@ located cmd (p_hsCmd' Applicand s) breakpoint inci $ located expr p_hsExpr- HsCmdLam _ variant mgroup -> p_lam isApp variant cmdPlacement p_hsCmd mgroup+ HsCmdLam _ variant mgroup -> p_lam isApp s variant cmdPlacement p_hsCmd mgroup HsCmdPar _ c -> parens N $ sitcc $ located c p_hsCmd HsCmdCase _ e mgroup ->- p_case isApp cmdPlacement p_hsCmd e mgroup+ p_case isApp s cmdPlacement p_hsCmd e mgroup HsCmdIf anns _ if' then' else' -> p_if cmdPlacement p_hsCmd anns if' then' else' HsCmdLet (letToken, _) localBinds c ->@@ -404,14 +415,15 @@ UnhelpfulSpan _ -> f x RealSrcSpan currentSpn _ -> do getSpanMark >>= \case- -- Spacing before comments will be handled by the code- -- that prints comments, so we just have to deal with- -- blank lines between statements here.- Just (StatementSpan lastSpn) ->- if srcSpanStartLine currentSpn > srcSpanEndLine lastSpn + 1- then newline- else return ()- _ -> return ()+ -- We deal with blank lines between statements here. The last mark+ -- may be a 'StatementSpan' (the usual case) or a comment span: the+ -- latter happens when the previous statement ended with a trailing+ -- comment, in which case we still want to preserve a blank line that+ -- followed that comment in the original input.+ Just lastMark ->+ let lastSpn = spanMarkSpan lastMark+ in when (srcSpanStartLine currentSpn > srcSpanEndLine lastSpn + 1) newline+ Nothing -> return () f x -- In some cases the (f x) expression may insert a new mark. We want -- to be careful not to override comment marks.@@ -565,8 +577,8 @@ p_dotFieldOcc = p_rdrName . fmap (mkVarUnqual . field_label) . dfoLabel -p_dotFieldOccs :: [DotFieldOcc GhcPs] -> R ()-p_dotFieldOccs = sep (txt ".") p_dotFieldOcc+p_dotFieldOccs :: NonEmpty (DotFieldOcc GhcPs) -> R ()+p_dotFieldOccs = sep (txt ".") p_dotFieldOcc . NE.toList p_fieldOcc :: FieldOcc GhcPs -> R () p_fieldOcc FieldOcc {..} = p_rdrName foLabel@@ -591,7 +603,7 @@ p_hsExpr = p_hsExpr' NotApplicand N -- | An applicand is the left-hand side in a function application, i.e. @f@ in--- @f a@. We need to track this in order to add extra identation in cases like+-- @f a@. We need to track this in order to add extra indentation in cases like -- -- > foo = -- > do@@ -604,10 +616,18 @@ Applicand -> inci . inci NotApplicand -> inci +-- | Adjust bracing as needed for certain cases e.g. involving case+-- expressions and lambdas.+adjustBracing :: IsApplicand -> BracketStyle -> R () -> R ()+adjustBracing isApp s p = do+ layout <- getLayout+ case (s, layout, isApp) of+ (S, SingleLine, NotApplicand) -> useBraces p+ _ -> p+ p_hsExpr' :: IsApplicand -> BracketStyle -> HsExpr GhcPs -> R () p_hsExpr' isApp s = \case HsVar _ name -> p_rdrName name- HsUnboundVar _ occ -> atom occ HsOverLabel sourceText _ -> do txt "#" p_sourceText sourceText@@ -622,7 +642,7 @@ HsMultilineString (SourceText stxt) _ -> p_stringLit stxt r -> atom r HsLam _ variant mgroup ->- p_lam isApp variant exprPlacement p_hsExpr mgroup+ p_lam isApp s variant exprPlacement p_hsExpr mgroup HsApp _ f x -> do let -- In order to format function applications with multiple parameters -- nicer, traverse the AST to gather the function and all the@@ -743,13 +763,14 @@ ExplicitSum _ tag arity e -> p_unboxedSum N tag arity (located e p_hsExpr) HsCase _ e mgroup ->- p_case isApp exprPlacement p_hsExpr e mgroup+ p_case isApp s exprPlacement p_hsExpr e mgroup HsIf anns if' then' else' -> p_if exprPlacement p_hsExpr anns if' then' else' HsMultiIf _ guards -> do txt "if" breakpoint- inciApplicand isApp $ sep breakpoint (located' (p_grhs RightArrow)) guards+ inciApplicand isApp $+ sep breakpoint (located' (p_grhs RightArrow)) (NE.toList guards) HsLet (letToken, _) localBinds e -> p_let (s == S) p_hsExpr letToken localBinds e HsDo _ doFlavor es -> do@@ -775,7 +796,7 @@ dotdot = case rec_dotdot of Just {} -> [txt ".."] Nothing -> []- inci . braces N $+ inci . recordBraces $ sep commaDel sitcc (fields <> dotdot) RecordUpd {..} -> do located rupd_expr p_hsExpr@@ -784,7 +805,7 @@ sep commaDel (sitcc . located' (p_hsFieldBind p_lbl)) p_fieldLabelStrings (FieldLabelStrings flss) = p_dotFieldOccs $ unLoc <$> flss- inci . braces N $ case rupd_flds of+ inci . recordBraces $ case rupd_flds of RegularRecUpdFields {..} -> p_recFields (located' p_fieldOcc) recUpdFields OverloadedRecUpdFields {..} ->@@ -795,7 +816,7 @@ located gf_field p_dotFieldOcc HsProjection {..} -> parens N $ do txt "."- p_dotFieldOccs (NE.toList proj_flds)+ p_dotFieldOccs proj_flds ExprWithTySig _ x HsWC {hswc_body} -> sitcc $ do located x p_hsExpr inci $ p_hsTypeAnnotation (hsSigTypeToType <$> hswc_body)@@ -828,7 +849,7 @@ breakpoint' txt "||]" HsUntypedBracket _ x -> p_hsQuote x- HsTypedSplice _ expr -> p_hsSpliceTH True expr DollarSplice+ HsTypedSplice _ (HsTypedSpliceExpr _ expr) -> p_hsSpliceTH True expr DollarSplice HsUntypedSplice _ untySplice -> p_hsUntypedSplice DollarSplice untySplice HsProc _ p e -> do txt "proc"@@ -855,6 +876,9 @@ txt "type" space located hswc_body p_hsType+ HsHole holeKind -> case holeKind of+ HoleVar name -> p_rdrName name+ HoleError -> error "parse error" -- similar to HsForAllTy expr@HsForAll {} -> p_hsFun expr@@ -962,7 +986,7 @@ -- will be ParStmt. [L _ (ParStmt _ blocks _ _)] -> [ concatMap collectNonParStmts stmts- | ParStmtBlock _ stmts _ _ <- blocks+ | ParStmtBlock _ stmts _ _ <- NE.toList blocks ] -- Otherwise, list will not contain any ParStmt stmts ->@@ -998,12 +1022,17 @@ breakpoint located psb_def p_pat breakpoint- txt "where"+ indentWhere <- getPrinterOpt poIndentWheres+ let (inciWhere, inciBody) =+ if indentWhere+ then (id, inci)+ else (inciByFrac (-1 / 2), id)+ inciWhere $ txt "where" breakpoint- inci (p_matchGroup (Function psb_id) mgroup)+ inciBody $ p_matchGroup (Function psb_id) mgroup txt "pattern" case psb_args of- PrefixCon [] xs -> do+ PrefixCon xs -> do space p_rdrName psb_id inci $ do@@ -1012,7 +1041,6 @@ unless (null xs) breakpoint sitcc (sep breakpoint p_rdrName xs) rhs conSpans- PrefixCon (v : _) _ -> absurd v RecCon xs -> do space p_rdrName psb_id@@ -1020,7 +1048,7 @@ let conSpans = getLocA . recordPatSynPatVar <$> xs switchLayout conSpans $ do unless (null xs) breakpointPreRecordBrace- braces N $+ recordBraces $ sep commaDel (p_rdrName . recordPatSynPatVar) xs rhs conSpans InfixCon l r -> do@@ -1040,6 +1068,7 @@ Anno (Match GhcPs (LocatedA body)) ~ SrcSpanAnnA ) => IsApplicand ->+ BracketStyle -> -- | Placer (body -> Placement) -> -- | Render@@ -1049,20 +1078,23 @@ -- | Match group MatchGroup GhcPs (LocatedA body) -> R ()-p_case isApp placer render e mgroup = do+p_case isApp s placer render e mgroup = do txt "case" space located e p_hsExpr space txt "of" breakpoint- inciApplicand isApp (p_matchGroup' placer render Case mgroup)+ adjustBracing isApp s $+ inciApplicand isApp (p_matchGroup' placer render Case mgroup) p_lam :: ( Anno (GRHS GhcPs (LocatedA body)) ~ EpAnnCO, Anno (Match GhcPs (LocatedA body)) ~ SrcSpanAnnA ) => IsApplicand ->+ -- | BracketStyle (S when inside a do block)+ BracketStyle -> -- | Variant (@\\@ or @\\case@ or @\\cases@) HsLamVariant -> -- | Placer@@ -1072,7 +1104,7 @@ -- | Expression MatchGroup GhcPs (LocatedA body) -> R ()-p_lam isApp variant placer render mgroup = do+p_lam isApp s variant placer render mgroup = do let mCaseTxt = case variant of LamSingle -> Nothing LamCase -> Just "\\case"@@ -1084,7 +1116,7 @@ Just caseTxt -> do txt caseTxt breakpoint- inciApplicand isApp pMatchGroup+ adjustBracing isApp s (inciApplicand isApp pMatchGroup) p_if :: -- | Placer@@ -1243,65 +1275,66 @@ _ -> Nothing p_pat :: Pat GhcPs -> R ()-p_pat = \case+p_pat = p_pat' False++p_pat' :: Bool -> Pat GhcPs -> R ()+p_pat' inAsPat = \case WildPat _ -> txt "_" VarPat _ name -> p_rdrName name LazyPat _ pat -> do txt "~"- located pat p_pat+ located pat (p_pat' inAsPat) AsPat _ name pat -> do p_rdrName name txt "@"- located pat p_pat+ located pat (p_pat' True) ParPat _ pat ->- located pat (parens S . sitcc . p_pat)+ located pat (parens S . sitcc . p_pat' inAsPat) BangPat _ pat -> do txt "!"- located pat p_pat+ located pat (p_pat' inAsPat) ListPat _ pats ->- brackets S $ sep commaDel (located' p_pat) pats+ brackets S $ sep commaDel (located' (p_pat' inAsPat)) pats TuplePat _ pats boxing -> do let parens' = case boxing of Boxed -> parens S Unboxed -> parensHash S- parens' $ sep commaDel (sitcc . located' p_pat) pats- OrPat _ pats ->- sepSemi (located' p_pat) (NE.toList pats)+ parens' $ sep commaDel (sitcc . located' (p_pat' inAsPat)) pats+ OrPat _ pats -> do+ sepSemi' inAsPat (located' (p_pat' inAsPat)) (NE.toList pats) SumPat _ pat tag arity ->- p_unboxedSum S tag arity (located pat p_pat)+ p_unboxedSum S tag arity (located pat (p_pat' inAsPat)) ConPat _ pat details -> case details of- PrefixCon tys xs -> sitcc $ do+ PrefixCon xs -> sitcc $ do p_rdrName pat- unless (null tys && null xs) breakpoint- inci . sitcc $- sep breakpoint (sitcc . either p_hsConPatTyArg (located' p_pat)) $- (Left <$> tys) <> (Right <$> xs)+ unless (null xs) breakpoint+ inci . sitcc $ sep breakpoint (sitcc . located' (p_pat' inAsPat)) xs RecCon (HsRecFields _ fields dotdot) -> do p_rdrName pat breakpointPreRecordBrace let f = \case Nothing -> txt ".." Just x -> located x p_pat_hsFieldBind- inci . braces N . sep commaDel f $+ inci . recordBraces . sep commaDel f $ case dotdot of Nothing -> Just <$> fields Just (L _ (RecFieldsDotDot n)) -> (Just <$> take n fields) ++ [Nothing] InfixCon l r -> do switchLayout [getLocA l, getLocA r] $ do- located l p_pat+ located l (p_pat' inAsPat) breakpoint inci $ do p_rdrName pat space- located r p_pat+ located r (p_pat' inAsPat) ViewPat _ expr pat -> sitcc $ do located expr p_hsExpr space token'rarrow breakpoint- inci (located pat p_pat)+ inci (located pat (p_pat' inAsPat)) SplicePat _ splice -> p_hsUntypedSplice DollarSplice splice LitPat _ p -> atom p NPat _ v (isJust -> isNegated) _ -> do@@ -1318,7 +1351,7 @@ space located k (atom . ol_val) SigPat _ pat HsPS {..} -> do- located pat p_pat+ located pat (p_pat' inAsPat) p_typeAscription (lhsTypeToSigType hsps_body) EmbTyPat _ (HsTP _ ty) -> do txt "type"@@ -1329,9 +1362,6 @@ p_tyPat :: HsTyPat GhcPs -> R () p_tyPat (HsTP _ ty) = txt "@" *> located ty p_hsType -p_hsConPatTyArg :: HsConPatTyArg GhcPs -> R ()-p_hsConPatTyArg (HsConPatTyArg _ patSigTy) = p_tyPat patSigTy- p_pat_hsFieldBind :: HsRecField GhcPs (LPat GhcPs) -> R () p_pat_hsFieldBind HsFieldBind {..} = do located hfbLHS p_fieldOcc@@ -1361,7 +1391,7 @@ HsUntypedSpliceExpr _ expr -> p_hsSpliceTH False expr deco HsQuasiQuote _ quoterName str -> do txt "["- p_rdrName (noLocA quoterName)+ p_rdrName quoterName txt "|" -- QuasiQuoters often rely on precise custom strings. We cannot do any -- formatting here without potentially breaking someone's code.@@ -1428,21 +1458,31 @@ -- | Function types in expressions, e.g. with -XRequiredTypeArguments instance FunRepr (HsExpr GhcPs) where- renderFunItem = p_hsExpr parseFunRepr = \case -- `forall a. _` L ann (HsForAll _ tele expr) ->- ParsedFunForall (L ann tele) (parseFunRepr expr)+ ParsedFunForall+ { tele = L ann tele,+ next = parseFunRepr expr+ } -- `HasCallStack => _` expr@(L _ HsQual {}) -> let (ctxs, rest) = getContexts expr- in ParsedFunQuals ctxs (parseFunRepr rest)+ in ParsedFunQuals+ { ctxs,+ next = parseFunRepr rest+ } -- `Int -> _`- expr@(L _ HsFunArr {}) ->- let (args, ret) = getArgsAndReturn expr- in ParsedFunArgs args (parseFunRepr ret)+ L ann (HsFunArr _ multAnn arg r) ->+ ParsedFunArg+ { span = ann,+ arg,+ doc = Nothing,+ multAnn,+ next = parseFunRepr r+ } -- `_ -> Int`- expr -> ParsedFunReturn (expr, Nothing)+ ret -> ParsedFunReturn {ret, doc = Nothing} where getContexts = let go ctxs = \case@@ -1451,14 +1491,12 @@ expr -> (reverse ctxs, expr) in go []- getArgsAndReturn =- let go args = \case- L ann (HsFunArr _ arrow l r) ->- go (L ann (l, Nothing, arrow) : args) r- expr ->- (reverse args, expr)- in go [] + renderFunReprArg = p_hsExpr+ renderFunReprCtx = p_hsExpr+ renderFunReprMult = p_hsExpr+ renderFunReprRet = p_hsExpr+ ---------------------------------------------------------------------------- -- Helpers @@ -1476,9 +1514,9 @@ -- | Determine placement of a given block. blockPlacement :: (body -> Placement) ->- [LGRHS GhcPs (LocatedA body)] ->+ NonEmpty (LGRHS GhcPs (LocatedA body)) -> Placement-blockPlacement placer [L _ (GRHS _ _ (L _ x))] = placer x+blockPlacement placer (L _ (GRHS _ _ (L _ x)) :| []) = placer x blockPlacement _ _ = Normal -- | Determine placement of a given command.@@ -1522,7 +1560,7 @@ _ -> Normal -- | Return 'True' if any of the RHS expressions has guards.-withGuards :: [LGRHS GhcPs body] -> Bool+withGuards :: NonEmpty (LGRHS GhcPs body) -> Bool withGuards = any (checkOne . unLoc) where checkOne (GRHS _ [] _) = False@@ -1531,10 +1569,12 @@ -- | For use before record braces. Collapse to empty if not 'poRecordBraceSpace'. breakpointPreRecordBrace :: R () breakpointPreRecordBrace = do+ recordStyle <- getPrinterOpt poRecordStyle useSpace <- getPrinterOpt poRecordBraceSpace- if useSpace- then breakpoint- else breakpoint'+ if+ | recordStyle == RecordStyleKnr -> space+ | useSpace -> breakpoint+ | otherwise -> breakpoint' -- | For nested lists/tuples, pad with whitespace so that we always indent correctly, -- rather than sometimes indenting by 2 regardless of 'poIndentation'.
src/Ormolu/Printer/Meat/ImportExport.hs view
@@ -8,6 +8,7 @@ module Ormolu.Printer.Meat.ImportExport ( p_hsmodExports, p_hsmodImport,+ enterMultilineLayoutIfContainsDocEntries, ) where @@ -28,12 +29,13 @@ p_hsmodExports :: [LIE GhcPs] -> R () p_hsmodExports xs =- parens' False $ do- layout <- getLayout- sep- breakpoint- (\(isAllPrevDoc, p, l) -> sitcc (located (addDocSrcSpan l) (p_lie layout isAllPrevDoc p)))- (withAllPrevDoc $ attachRelativePos xs)+ enterMultilineLayoutIfContainsDocEntries xs $+ parens' False $ do+ layout <- getLayout+ sep+ breakpoint+ (\(isAllPrevDoc, p, l) -> sitcc (located (addDocSrcSpan l) (p_lie layout isAllPrevDoc p)))+ (withAllPrevDoc $ attachRelativePos xs) where -- In order to correctly set the layout when a doc comment is present. addDocSrcSpan lie@(L l ie) = case ieExportDoc ie of@@ -49,6 +51,10 @@ space when ideclSafe (txt "safe") space+ case ideclLevelSpec of+ LevelStylePre l -> p_declLevel l+ _ -> return ()+ space when (isImportDeclQualified ideclQualified && not useQualifiedPost) (txt "qualified")@@ -59,6 +65,10 @@ space inci $ do located ideclName atom+ space+ case ideclLevelSpec of+ LevelStylePost l -> p_declLevel l+ _ -> return () when (isImportDeclQualified ideclQualified && useQualifiedPost) (space >> txt "qualified")@@ -85,6 +95,11 @@ (attachRelativePos xs) newline +p_declLevel :: ImportDeclLevel -> R ()+p_declLevel = \case+ ImportDeclSplice -> txt "splice"+ ImportDeclQuote -> txt "quote"+ p_lie :: Layout -> Bool -> RelativePos -> IE GhcPs -> R () p_lie encLayout isAllPrevDoc relativePos = \case IEVar mwarn l1 exportDoc -> do@@ -182,6 +197,19 @@ IEGroup {} -> Nothing IEDoc {} -> Nothing IEDocNamed {} -> Nothing++enterMultilineLayoutIfContainsDocEntries :: [LIE GhcPs] -> R () -> R ()+enterMultilineLayoutIfContainsDocEntries xs =+ if any (isDocEntry . unLoc) xs+ then enterLayout MultiLine+ else id++isDocEntry :: (IE pass) -> Bool+isDocEntry = \case+ IEDoc {} -> True+ IEGroup {} -> True+ IEDocNamed {} -> True+ _ -> False ----------------------------------------------------------------------------
src/Ormolu/Printer/Meat/Module.hs view
@@ -12,7 +12,9 @@ import Control.Monad import Data.Choice (pattern With)+import Data.Choice qualified as Choice import GHC.Hs hiding (comment)+import GHC.LanguageExtensions (Extension (ImplicitPrelude)) import GHC.Types.SrcLoc import Ormolu.Config import Ormolu.Imports (normalizeImports)@@ -40,27 +42,39 @@ let XModulePs {..} = hsmodExt deprecSpan = maybe [] (pure . getLocA) hsmodDeprecMessage exportSpans = maybe [] (pure . getLocA) hsmodExports- switchLayout (deprecSpan <> exportSpans) $ do- forM_ mstackHeader $ \(L spn comment) -> do- spitCommentNow spn comment+ switchLayout (deprecSpan <> exportSpans) $+ enterMultilineLayoutIfContainsDocEntries (maybe [] unLoc hsmodExports) $ do+ forM_ mstackHeader $ \(L spn comment) -> do+ spitCommentNow spn comment+ newline newline- newline- p_pragmas pragmas- newline- mapM_ (p_hsModuleHeader hsmod) hsmodName- newline- respectful <- getPrinterOpt poRespectful- localModules <- getLocalModules- importGrouping <- getPrinterOpt poImportGrouping- forM_ (normalizeImports respectful localModules importGrouping hsmodImports) $ \importGroup -> do- forM_ importGroup (located' p_hsmodImport)+ p_pragmas pragmas newline- declNewline- switchLayout (getLocA <$> hsmodDecls) $ do- preserveSpacing <- getPrinterOpt poRespectful- (if preserveSpacing then p_hsDeclsRespectGrouping else p_hsDecls) Free hsmodDecls+ mapM_ (p_hsModuleHeader hsmod) hsmodName newline- spitRemainingComments+ importGroups <- normalizeImportsR hsmodImports+ forM_ importGroups $ \importGroup -> do+ forM_ importGroup (located' p_hsmodImport)+ newline+ declNewline+ switchLayout (getLocA <$> hsmodDecls) $ do+ preserveSpacing <- getPrinterOpt poRespectful+ (if preserveSpacing then p_hsDeclsRespectGrouping else p_hsDecls) Free hsmodDecls+ newline+ spitRemainingComments+ where+ normalizeImportsR imports = do+ implicitPrelude <- Choice.fromBool <$> isExtensionEnabled ImplicitPrelude+ respectful <- getPrinterOpt poRespectful+ localModules <- getLocalModules+ importGrouping <- getPrinterOpt poImportGrouping+ pure $+ normalizeImports+ implicitPrelude+ respectful+ localModules+ importGrouping+ imports p_hsModuleHeader :: HsModule GhcPs -> LocatedA ModuleName -> R () p_hsModuleHeader HsModule {hsmodExt = XModulePs {..}, ..} moduleName = do
src/Ormolu/Printer/Meat/Type.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_GHC -Wno-orphans #-} -- | Rendering of types. module Ormolu.Printer.Meat.Type@@ -16,42 +18,40 @@ p_hsTyVarBndr, ForAllVisibility (..), p_forallBndrs,- p_conDeclFields,+ p_hsConDeclRecFields,+ p_hsConDeclField,+ p_hsConDeclFieldWithDoc, p_lhsTypeArg, p_hsSigType,- FunRepr (..),- ParsedFunRepr (..),- p_hsFun,- hsOuterTyVarBndrsToHsType, hsSigTypeToType, lhsTypeToSigType,++ -- * Re-exports from Ormolu.Printer.Meat.Type.Function+ FunRepr (..),+ ParsedFunRepr,+ ParsedFunRepr' (..),+ p_hsFun, ) where import Control.Monad-import Control.Monad.Cont qualified as Cont-import Control.Monad.State qualified as State-import Control.Monad.Trans qualified as Trans-import Data.Choice (Choice, pattern Is, pattern Isn't, pattern With, pattern Without)-import Data.Choice qualified as Choice-import Data.Foldable (traverse_)-import Data.Functor ((<&>))-import Data.List (sortOn)-import Data.Maybe (isJust)+import Data.Choice (pattern Is, pattern With, pattern Without)+import Data.Maybe (fromMaybe) import GHC.Data.Strict qualified as Strict import GHC.Hs hiding (isPromoted) import GHC.Types.SourceText import GHC.Types.SrcLoc import GHC.Types.Var-import GHC.Utils.Outputable (Outputable) import Ormolu.Config import Ormolu.Printer.Combinators import Ormolu.Printer.Meat.Common import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.OpTree (p_tyOpTree, tyOpTree) import Ormolu.Printer.Meat.Declaration.StringLiteral import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.Value (p_hsUntypedSplice)+import Ormolu.Printer.Meat.Type.Function import Ormolu.Printer.Operators import Ormolu.Utils+import Prelude hiding (span) p_hsType :: HsType GhcPs -> R () p_hsType = \case@@ -137,31 +137,8 @@ -- String -- -- | Age -- Int- usePipe <-- getPrinterOpt poFunctionArrows <&> \case- TrailingArrows -> True- LeadingArrows -> False- LeadingArgsArrows -> False- if usePipe- then do- p_hsDoc Pipe (With #endNewline) str- located t p_hsType- else do- located t p_hsType- newline- p_hsDoc Caret (Without #endNewline) str- HsBangTy _ (HsBang u s) t -> do- case u of- SrcUnpack -> txt "{-# UNPACK #-}" >> space- SrcNoUnpack -> txt "{-# NOUNPACK #-}" >> space- NoSrcUnpack -> return ()- case s of- SrcLazy -> txt "~"- SrcStrict -> txt "!"- NoSrcStrict -> return ()- located t p_hsType- HsRecTy _ fields ->- p_conDeclFields fields+ withHaddocks (Is #end) (Just str) $ do+ located t p_hsType HsExplicitListTy _ p xs -> do case p of IsPromoted -> txt "'"@@ -189,7 +166,20 @@ HsStrTy (SourceText s) _ -> p_stringLit s a -> atom a HsWildCardTy _ -> txt "_"- XHsType t -> atom t+ XHsType ext -> case ext of+ HsCoreTy t -> atom @HsCoreTy t+ HsBangTy _ (HsSrcBang _ u s) t -> do+ case u of+ SrcUnpack -> txt "{-# UNPACK #-}" >> space+ SrcNoUnpack -> txt "{-# NOUNPACK #-}" >> space+ NoSrcUnpack -> return ()+ case s of+ SrcLazy -> txt "~"+ SrcStrict -> txt "!"+ NoSrcStrict -> return ()+ located t p_hsType+ HsRecTy _ fields ->+ p_hsConDeclRecFields fields where startsWithSingleQuote = \case HsAppTy _ (L _ f) _ -> startsWithSingleQuote f@@ -200,7 +190,12 @@ _ -> False p_hsTypeAnnotation :: LHsType GhcPs -> R ()-p_hsTypeAnnotation = p_hsFunParsed . ParsedFunSig . parseFunRepr+p_hsTypeAnnotation ty =+ p_hsFunParsed @(HsType GhcPs) $+ ParsedFunSig+ { sig = (),+ next = parseFunRepr ty+ } -- | Return 'True' if at least one argument in 'HsType' has a doc string -- attached to it.@@ -215,101 +210,47 @@ p_hsContext :: HsContext GhcPs -> R () p_hsContext = p_hsContext' p_hsType -p_hsContext' ::- (Outputable (GenLocated (Anno a) a), HasLoc (Anno a)) =>- (a -> R ()) ->- [XRec GhcPs a] ->- R ()-p_hsContext' f = \case- [] -> txt "()"- [x] -> located x f- xs -> do- shouldSort <- getPrinterOpt poSortConstraints- let sort = if shouldSort then sortOn showOutputable else id- parens N $ sep commaDel (sitcc . located' f) (sort xs)--class IsTyVarBndrFlag flag where- isInferred :: flag -> Bool- p_tyVarBndrFlag :: flag -> R ()- p_tyVarBndrFlag _ = pure ()--instance IsTyVarBndrFlag () where- isInferred () = False--instance IsTyVarBndrFlag Specificity where- isInferred = \case- InferredSpec -> True- SpecifiedSpec -> False--instance IsTyVarBndrFlag (HsBndrVis GhcPs) where- isInferred _ = False- p_tyVarBndrFlag = \case- HsBndrRequired NoExtField -> pure ()- HsBndrInvisible _ -> txt "@"--p_hsTyVarBndr :: (IsTyVarBndrFlag flag) => HsTyVarBndr flag GhcPs -> R ()-p_hsTyVarBndr HsTvb {..} = do- p_tyVarBndrFlag tvb_flag- let wrap- | isInferred tvb_flag = braces N- | otherwise = case tvb_kind of- HsBndrKind {} -> parens N- HsBndrNoKind {} -> id- wrap $ do- case tvb_var of- HsBndrVar _ x -> p_rdrName x- HsBndrWildCard _ -> txt "_"- case tvb_kind of- HsBndrKind _ k -> inci $ p_hsTypeAnnotation k- HsBndrNoKind _ -> pure ()--data ForAllVisibility = ForAllInvis | ForAllVis---- | Render several @forall@-ed variables.-p_forallBndrs ::- (HasLoc l) =>- ForAllVisibility ->- (a -> R ()) ->- [GenLocated l a] ->- R ()-p_forallBndrs vis p tyvars = do- p_forallBndrsStart p tyvars- p_forallBndrsEnd (Without #extraSpace) vis--p_forallBndrsStart :: (HasLoc l) => (a -> R ()) -> [GenLocated l a] -> R ()-p_forallBndrsStart _ [] = token'forall-p_forallBndrsStart p tyvars = do- switchLayout (locA <$> tyvars) $ do- token'forall- breakpoint- inci $ do- sitcc $ sep breakpoint (sitcc . located' p) tyvars--p_forallBndrsEnd :: Choice "extraSpace" -> ForAllVisibility -> R ()-p_forallBndrsEnd extraSpace = \case- ForAllInvis -> txt dot >> space- ForAllVis -> space >> token'rarrow- where- dot = if Choice.isTrue extraSpace then " ." else "."--p_conDeclFields :: [LConDeclField GhcPs] -> R ()-p_conDeclFields xs =- braces N $ sep commaDel (sitcc . located' p_conDeclField) xs+p_hsConDeclRecFields :: [LHsConDeclRecField GhcPs] -> R ()+p_hsConDeclRecFields xs =+ recordBraces $ sep commaDel (sitcc . located' p_hsConDeclRecField) xs -p_conDeclField :: ConDeclField GhcPs -> R ()-p_conDeclField ConDeclField {..} = do- commaStyle <- getPrinterOpt poCommaStyle- when (commaStyle == Trailing) $- mapM_ (p_hsDoc Pipe (With #endNewline)) cd_fld_doc+p_hsConDeclRecField :: HsConDeclRecField GhcPs -> R ()+p_hsConDeclRecField field@HsConDeclRecField {..} = withFieldHaddocks $ do sitcc $ sep commaDel (located' (p_rdrName . foLabel))- cd_fld_names- inci $ p_hsTypeAnnotation cd_fld_type- when (commaStyle == Leading) $- mapM_ (inciByFrac (-1) . (newline >>) . p_hsDoc Caret (Without #endNewline)) cd_fld_doc+ cdrf_names+ inci $ p_hsFun field+ where+ withFieldHaddocks action = do+ commaStyle <- getPrinterOpt poCommaStyle+ let doc = cdf_doc cdrf_spec+ when (commaStyle == Trailing) $+ mapM_ (p_hsDoc Pipe (With #endNewline)) doc+ action+ when (commaStyle == Leading) $+ mapM_ (inciByFrac (-1) . (newline >>) . p_hsDoc Caret (Without #endNewline)) doc +-- | This does not print 'cdf_doc' and 'cdf_multiplicity' as there is no single+-- strategy for where to print them (see call sites).+p_hsConDeclField :: HsConDeclField GhcPs -> R ()+p_hsConDeclField CDF {..} = do+ case cdf_unpack of+ SrcUnpack -> txt "{-# UNPACK #-}" *> space+ SrcNoUnpack -> txt "{-# NOUNPACK #-}" *> space+ NoSrcUnpack -> pure ()+ located cdf_type $ \ty -> do+ case cdf_bang of+ SrcLazy -> txt "~"+ SrcStrict -> txt "!"+ NoSrcStrict -> pure ()+ p_hsType ty++p_hsConDeclFieldWithDoc :: HsConDeclField GhcPs -> R ()+p_hsConDeclFieldWithDoc cdf = withHaddocks (Is #end) cdf.cdf_doc $ do+ p_hsConDeclField cdf+ p_lhsTypeArg :: LHsTypeArg GhcPs -> R () p_lhsTypeArg = \case HsValArg NoExtField ty -> located ty p_hsType@@ -322,54 +263,37 @@ p_hsSigType :: HsSigType GhcPs -> R () p_hsSigType = p_hsType . hsSigTypeToType -------------------------------------------------------------------------------- Rendering function types---- | The parsed representation of a function-data ParsedFunRepr a- = ParsedFunSig (ParsedFunRepr a)- | ParsedFunForall- (LocatedA (HsForAllTelescope GhcPs))- (ParsedFunRepr a)- | ParsedFunQuals- [LocatedA (LocatedC [LocatedA a])]- (ParsedFunRepr a)- | -- | The argument, its optional docstring, and the arrow going to the next arg/return- ParsedFunArgs- [ LocatedA- ( LocatedA a,- Maybe (LHsDoc GhcPs),- HsArrowOf (LocatedA a) GhcPs- )- ]- (ParsedFunRepr a)- | ParsedFunReturn- ( LocatedA a,- Maybe (LHsDoc GhcPs)- )--class (Anno a ~ SrcSpanAnnA, Outputable a) => FunRepr a where- renderFunItem :: a -> R ()-- parseFunRepr :: LocatedA a -> ParsedFunRepr a- instance FunRepr (HsType GhcPs) where- renderFunItem = p_hsType parseFunRepr = \case -- `forall a. _` L ann (HsForAllTy _ tele ty) ->- ParsedFunForall (L ann tele) (parseFunRepr ty)+ ParsedFunForall+ { tele = L ann tele,+ next = parseFunRepr ty+ } -- `HasCallStack => _` ty@(L _ HsQualTy {}) -> let (ctxs, rest) = getContexts ty- in ParsedFunQuals ctxs (parseFunRepr rest)+ in ParsedFunQuals+ { ctxs,+ next = parseFunRepr rest+ } -- `Int -> _`- ty@(L _ HsFunTy {}) ->- let (args, ret) = getArgsAndReturn ty- in ParsedFunArgs args (parseFunRepr ret)+ L ann (HsFunTy _ multAnn l r) ->+ let (arg, doc) =+ case l of+ L _ (HsDocTy _ x doc_) -> (x, Just doc_)+ _ -> (l, Nothing)+ in ParsedFunArg+ { span = ann,+ arg,+ doc,+ multAnn,+ next = parseFunRepr r+ } -- `_ -> Int`- L _ (HsDocTy _ ty doc) -> ParsedFunReturn (ty, Just doc)- ty -> ParsedFunReturn (ty, Nothing)+ L _ (HsDocTy _ ret doc) -> ParsedFunReturn {ret, doc = Just doc}+ ret -> ParsedFunReturn {ret, doc = Nothing} where getContexts = let go ctxs = \case@@ -378,203 +302,106 @@ ty -> (reverse ctxs, ty) in go []- getArgsAndReturn =- let go args = \case- L ann (HsFunTy _ arrow (L _ (HsDocTy _ l doc)) r) ->- go (L ann (l, Just doc, arrow) : args) r- L ann (HsFunTy _ arrow l r) ->- go (L ann (l, Nothing, arrow) : args) r- ty ->- (reverse args, ty)- in go [] --- | For implementing function-arrows and related configuration, we'll collect--- all the components of the function type first, then render as a block instead--- of rendering each part independently, which will let us track local state--- within a function type.------ This function should be passed the first function-related construct we find;--- see FunRepr for more details.-p_hsFun :: (FunRepr a) => a -> R ()-p_hsFun = p_hsFunParsed . parseFunRepr . L (noAnn @SrcSpanAnnA)--p_hsFunParsed :: (FunRepr a) => ParsedFunRepr a -> R ()-p_hsFunParsed fun = do- arrowsStyle <- getPrinterOpt poFunctionArrows- p_hsFunParsed' arrowsStyle fun--type PrintHsFun x = State.StateT PrintHsFunState (Cont.ContT () R) x--type PrintHsFunState =- ( Maybe (R ()), -- The leading delimiter to output at the next line- Choice "forceMultiline"- )--p_hsFunParsed' ::- (FunRepr a) =>- FunctionArrowsStyle ->- ParsedFunRepr a ->- R ()-p_hsFunParsed' arrowsStyle fun0 = Cont.evalContT . (`State.evalStateT` initialState) . go $ fun0- where- go = \case- ParsedFunSig fun' -> do- -- Should only happen at the very beginning, if at all- p_parsedFunSig- setMultilineContext fun'- go fun'- ParsedFunForall tele fun' -> do- p_parsedFunForall tele- setMultilineContext fun'- go fun'- ParsedFunQuals ctxs fun' -> do- p_parsedFunQuals ctxs- setMultilineContext fun'- go fun'- ParsedFunArgs args fun' -> do- p_parsedFunArgs args- go fun'- ParsedFunReturn ret -> do- p_parsedFunReturn ret-- liftR = Trans.lift . Trans.lift- initialState = (Nothing, Without #forceMultiline)-- withApplyLeadingDelim f = do- (leadingDelim, _) <- State.get- a <- f leadingDelim- State.modify $ \(_, x) -> (Nothing, x)- pure a- applyLeadingDelim = withApplyLeadingDelim (traverse_ liftR)-- setLeadingDelim delim =- State.modify $ \(_, x) -> (Just delim, x)-- getIsMultiline = do- (_, forceMultiline) <- State.get- layout <- liftR getLayout- pure $ Choice.toBool forceMultiline || layout == MultiLine-- setMultilineContext fun =- State.modify $ \(x, _) -> (x, Choice.fromBool $ hasDocs fun)-- -- Make everything afterwards occur within a located block, with the- -- magic of ContT. `item <- setLocated litem; ...` is equivalent to- -- `located litem $ \item -> ...`.- setLocated :: (HasLoc ann) => GenLocated ann x -> PrintHsFun x- setLocated litem = Trans.lift $ Cont.ContT (located litem)-- p_parsedFunSig = do- if isTrailing (Isn't #argDelim)- then liftR $ do- space >> token'dcolon- if hasDocs fun0 then newline else breakpoint- else do- liftR breakpoint- setLeadingDelim (token'dcolon >> space)-- p_parsedFunForall x@(L _ tele) = do- void $ setLocated x- applyLeadingDelim- vis <-- case tele of- HsForAllInvis _ bndrs -> do- liftR $ p_forallBndrsStart p_hsTyVarBndr bndrs- pure ForAllInvis- HsForAllVis _ bndrs -> do- liftR $ p_forallBndrsStart p_hsTyVarBndr bndrs- pure ForAllVis- isMultiline <- getIsMultiline- if isTrailing (Isn't #argDelim) || not isMultiline- then do- liftR $ p_forallBndrsEnd (Without #extraSpace) vis- interArgBreak- else do- interArgBreak- let extraSpace = if isMultiline then With #extraSpace else Without #extraSpace- setLeadingDelim (p_forallBndrsEnd extraSpace vis)-- p_parsedFunQuals ctxs = do- -- we only want to set located on the first context- case ctxs of- ctx : _ -> void $ setLocated ctx- _ -> pure ()-- forM_ ctxs $ \(L _ lctx) -> do- applyLeadingDelim- liftR $ located lctx (p_hsContext' renderFunItem)- if isTrailing (Isn't #argDelim)- then do- liftR $ space >> token'darrow- interArgBreak- else do- interArgBreak- setLeadingDelim (token'darrow >> space)-- p_parsedFunArgs args = do- -- we only want to set located on the first arg- case args of- arg : _ -> void $ setLocated arg- _ -> pure ()+ renderFunReprCtx = p_hsType+ renderFunReprArg = p_hsType+ renderFunReprMult = p_hsType+ renderFunReprRet = p_hsType - forM_ args $ \(L _ (larg, doc, arrow)) -> do- let renderArrow = p_arrow (located' renderFunItem) arrow- withHaddocks (Isn't #end) doc $ do- withApplyLeadingDelim $ \applyLeadingDelim' ->- liftR . located larg $ \arg -> do- traverse_ id applyLeadingDelim'- renderFunItem arg- if isTrailing (Is #argDelim)- then do- liftR $ space >> renderArrow- interArgBreak- else do- interArgBreak- setLeadingDelim (renderArrow >> space)+----------------------------------------------------------------------------+-- FunRepr HsConDeclRecField - p_parsedFunReturn (ret, doc) = do- void $ setLocated ret- withHaddocks (Is #end) doc $ do- applyLeadingDelim- liftR $ located ret renderFunItem+-- | FunRepr HsConDeclRecField renders a record field type annotation. If the+-- record field has a function type, we want to render it as a function+-- respecting `function-arrows`. For the most part, it behaves like HsTypeSig;+-- however, record fields can have additional syntax not in normal function+-- types, like specifying the multiplicity for the `::` or specifying+-- UNPACK/strictness, so we need to handle this specially.+instance FunRepr (HsConDeclRecField GhcPs) where+ parseFunRepr (L _ HsConDeclRecField {..}) =+ ParsedFunSig+ { sig = cdrf_spec.cdf_multiplicity,+ next = toRecFieldRepr False $ parseFunRepr cdrf_spec.cdf_type+ }+ where+ toRecFieldRepr :: Bool -> ParsedFunRepr (HsType GhcPs) -> ParsedFunRepr (HsConDeclRecField GhcPs)+ toRecFieldRepr seenArg = \case+ ParsedFunSig {} -> error "parseFunRepr @HsType unexpectedly returned ParsedFunSig"+ ParsedFunForall {..} ->+ ParsedFunForall+ { tele,+ next = toRecFieldRepr seenArg next+ }+ ParsedFunQuals {..} ->+ ParsedFunQuals+ { ctxs,+ next = toRecFieldRepr seenArg next+ }+ ParsedFunArg {..} ->+ ParsedFunArg+ { span,+ arg =+ let mUnpack =+ if not seenArg+ then Just cdrf_spec.cdf_unpack+ else Nothing+ in L (getLoc arg) (mUnpack, arg),+ doc,+ multAnn,+ next = toRecFieldRepr True next+ }+ ParsedFunReturn {..} ->+ ParsedFunReturn+ { ret =+ let mAnn =+ if not seenArg+ then Just (cdrf_spec.cdf_unpack, cdrf_spec.cdf_bang)+ else Nothing+ in L (getLoc ret) (mAnn, ret),+ doc+ } - withHaddocks isEnd doc m = do- isLeadingHaddock <-- liftR $- getPrinterOpt poHaddockLocSignature <&> \case- HaddockLocSigAuto ->- if isTrailing (Is #argDelim) then With #pipe else Without #pipe- HaddockLocSigLeading ->- With #pipe- HaddockLocSigTrailing ->- Without #pipe+ type FunReprSig (HsConDeclRecField GhcPs) = HsMultAnnOf (LocatedA (HsType GhcPs)) GhcPs+ renderFunReprSig multAnn = do+ space+ p_hsMultAnn (located' p_hsType) multAnn+ space+ token'dcolon - if Choice.isTrue isLeadingHaddock- then do- traverse (liftR . p_hsDoc Pipe (With #endNewline)) doc *> m- else do- let (pre, endNewline) =- if Choice.isTrue isEnd- then (when (isJust doc) (liftR newline), Without #endNewline)- else (pure (), With #endNewline)- m <* pre <* traverse (liftR . p_hsDoc Caret endNewline) doc+ type FunReprCtx (HsConDeclRecField GhcPs) = HsType GhcPs+ renderFunReprCtx = p_hsType - interArgBreak = do- isMultiline <- getIsMultiline- liftR $ if isMultiline then newline else breakpoint+ -- Invariant: SrcUnpackedness should only be set for the first arg+ type FunReprArg (HsConDeclRecField GhcPs) = (Maybe SrcUnpackedness, LocatedA (HsType GhcPs))+ renderFunReprArg (mUnpacked, ty) =+ p_hsConDeclField+ CDF+ { cdf_ext = error "unused",+ cdf_unpack = fromMaybe NoSrcUnpack mUnpacked,+ cdf_bang = NoSrcStrict,+ cdf_multiplicity = error "unused",+ cdf_type = ty,+ cdf_doc = error "unused"+ } - hasDocs = \case- ParsedFunSig fun -> hasDocs fun- ParsedFunForall _ fun -> hasDocs fun- ParsedFunQuals _ fun -> hasDocs fun- ParsedFunArgs args fun -> or [isJust doc | L _ (_, doc, _) <- args] || hasDocs fun- ParsedFunReturn (_, doc) -> isJust doc+ type FunReprMult (HsConDeclRecField GhcPs) = HsType GhcPs+ renderFunReprMult = p_hsType - isTrailing isArgDelim =- case arrowsStyle of- TrailingArrows -> True- LeadingArgsArrows -> not $ Choice.isTrue isArgDelim- LeadingArrows -> False+ -- Invariant: SrcUnpackedness/SrcStrictness should only be set if there are+ -- no args+ type FunReprRet (HsConDeclRecField GhcPs) = (Maybe (SrcUnpackedness, SrcStrictness), LocatedA (HsType GhcPs))+ renderFunReprRet (mAnn, ty) =+ p_hsConDeclField+ CDF+ { cdf_ext = error "unused",+ cdf_unpack = unpack,+ cdf_bang = strictness,+ cdf_multiplicity = error "unused",+ cdf_type = ty,+ cdf_doc = error "unused"+ }+ where+ (unpack, strictness) = fromMaybe (NoSrcUnpack, NoSrcStrict) mAnn ---------------------------------------------------------------------------- -- Conversion functions
+ src/Ormolu/Printer/Meat/Type/Function.hs view
@@ -0,0 +1,493 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoFieldSelectors #-}+{-# OPTIONS_GHC -Wno-partial-fields #-}++-- | A Fourmolu-specific module for rendering function-like type signatures.+module Ormolu.Printer.Meat.Type.Function+ ( -- * PrintHsFun+ PrintHsFun,+ MonadR,++ -- * ParsedFunRepr+ ParsedFunRepr,+ ParsedFunRepr' (..),+ FunRepr (..),+ p_hsFun,+ p_hsFunParsed,++ -- * forall+ ForAllVisibility (..),+ p_forallBndrs,++ -- * TyVarBndr+ p_hsTyVarBndr,++ -- * Contexts+ p_hsContext',++ -- * Haddocks+ withHaddocks,+ )+where++import Control.Monad (forM_, void, when)+import Control.Monad.Cont qualified as Cont+import Control.Monad.State qualified as State+import Control.Monad.Trans qualified as Trans+import Data.Choice (Choice, pattern Is, pattern Isn't, pattern With, pattern Without)+import Data.Choice qualified as Choice+import Data.Foldable (traverse_)+import Data.Functor ((<&>))+import Data.List (sortOn)+import Data.Maybe (isJust)+import GHC.Hs+import GHC.Types.SrcLoc (GenLocated (..))+import GHC.Types.Var (Specificity (..))+import GHC.Utils.Outputable (Outputable)+import Ormolu.Config+import Ormolu.Printer.Combinators+import Ormolu.Printer.Meat.Common (p_arrow, p_hsDoc, p_rdrName)+import Ormolu.Utils (showOutputable)+import Prelude hiding (span)++{----- PrintHsFun -----}++newtype PrintHsFun a = PrintHsFun (State.StateT PrintHsFunState (Cont.ContT () R) a)+ deriving newtype (Functor, Applicative, Monad)++data PrintHsFunState = PrintHsFunState+ { -- | The leading delimiter to output at the next line+ leadingDelim :: Maybe (R ()),+ forceMultiline :: Bool,+ -- | Whether we're currently printing args+ inArgList :: Bool+ }++runPrintHsFun :: PrintHsFun () -> R ()+runPrintHsFun (PrintHsFun m) = Cont.evalContT . (`State.evalStateT` initialState) $ m+ where+ initialState =+ PrintHsFunState+ { leadingDelim = Nothing,+ forceMultiline = False,+ inArgList = False+ }++class (Monad m) => MonadR m where+ liftR :: R a -> m a++instance MonadR R where+ liftR = id++instance MonadR PrintHsFun where+ liftR = PrintHsFun . Trans.lift . Trans.lift++withApplyLeadingDelim :: (Maybe (R ()) -> PrintHsFun a) -> PrintHsFun a+withApplyLeadingDelim f = do+ state <- PrintHsFun State.get+ a <- f state.leadingDelim+ PrintHsFun . State.put $ state {leadingDelim = Nothing}+ pure a++applyLeadingDelim :: PrintHsFun ()+applyLeadingDelim = withApplyLeadingDelim (traverse_ liftR)++setLeadingDelim :: R () -> PrintHsFun ()+setLeadingDelim delim =+ PrintHsFun . State.modify $ \state -> state {leadingDelim = Just delim}++getIsMultiline :: PrintHsFun Bool+getIsMultiline = do+ state <- PrintHsFun State.get+ layout <- liftR getLayout+ pure $ state.forceMultiline || layout == MultiLine++setMultilineContext :: ParsedFunRepr' sig ctx arg mult ret -> PrintHsFun ()+setMultilineContext fun =+ PrintHsFun . State.modify $ \state -> state {forceMultiline = hasDocs fun}++getIsTrailing :: (MonadR m) => m (Choice "argDelim" -> Bool)+getIsTrailing = do+ arrowsStyle <- liftR $ getPrinterOpt poFunctionArrows+ pure $ \isArgDelim ->+ case arrowsStyle of+ TrailingArrows -> True+ LeadingArgsArrows -> not $ Choice.isTrue isArgDelim+ LeadingArrows -> False++getInArgList :: PrintHsFun Bool+getInArgList = PrintHsFun . State.gets $ (.inArgList)++setInArgList :: Bool -> PrintHsFun ()+setInArgList x = PrintHsFun . State.modify $ \state -> state {inArgList = x}++-- Make everything afterwards occur within a located block, with the+-- magic of ContT. `item <- setLocated litem; ...` is equivalent to+-- `located litem $ \item -> ...`.+setLocated :: (HasLoc ann) => GenLocated ann x -> PrintHsFun x+setLocated litem = PrintHsFun . Trans.lift $ Cont.ContT (located litem)++setLocated_ :: (HasLoc ann) => GenLocated ann x -> PrintHsFun ()+setLocated_ = void . setLocated++{----- ParsedFunRepr -----}++type ParsedFunRepr a =+ ParsedFunRepr'+ (FunReprSig a)+ (FunReprCtx a)+ (FunReprArg a)+ (FunReprMult a)+ (FunReprRet a)++-- | The parsed representation of a function+data ParsedFunRepr' sig ctx arg mult ret+ = -- | The "::" delimiter. Invariant: must be first.+ ParsedFunSig+ { sig :: sig,+ next :: ParsedFunRepr' sig ctx arg mult ret+ }+ | -- | The "forall a b." or "forall a b ->" construct.+ ParsedFunForall+ { tele :: LocatedA (HsForAllTelescope GhcPs),+ next :: ParsedFunRepr' sig ctx arg mult ret+ }+ | -- | The "(A, B) =>" construct.+ ParsedFunQuals+ { ctxs :: [LocatedA (LocatedC [LocatedA ctx])],+ next :: ParsedFunRepr' sig ctx arg mult ret+ }+ | ParsedFunArg+ { span :: SrcSpanAnnA,+ arg :: LocatedA arg,+ doc :: Maybe (LHsDoc GhcPs),+ multAnn :: HsMultAnnOf (LocatedA mult) GhcPs,+ next :: ParsedFunRepr' sig ctx arg mult ret+ }+ | ParsedFunReturn+ { ret :: LocatedA ret,+ doc :: Maybe (LHsDoc GhcPs)+ }++class+ ( Anno (FunReprCtx a) ~ SrcSpanAnnA,+ Outputable (FunReprCtx a)+ ) =>+ FunRepr a+ where+ parseFunRepr :: LocatedA a -> ParsedFunRepr a++ type FunReprSig a+ type FunReprSig a = ()+ renderFunReprSig :: FunReprSig a -> R ()+ default renderFunReprSig :: (FunReprSig a ~ ()) => FunReprSig a -> R ()+ renderFunReprSig () = space *> token'dcolon++ type FunReprCtx a+ type FunReprCtx a = a+ renderFunReprCtx :: FunReprCtx a -> R ()++ type FunReprArg a+ type FunReprArg a = a+ renderFunReprArg :: FunReprArg a -> R ()++ type FunReprMult a+ type FunReprMult a = a+ renderFunReprMult :: FunReprMult a -> R ()++ type FunReprRet a+ type FunReprRet a = a+ renderFunReprRet :: FunReprRet a -> R ()++-- | Some functions rely on HsType having a FunRepr instance, but the instance+-- can't be implemented here, since p_hsType is implemented in Type.hs, which+-- would cause a circular dependency. So we'll add it as an explicit constraint+-- and the call-site should bring the FunRepr instance for HsType in scope.+type FunReprHsType =+ ( FunRepr (HsType GhcPs),+ FunReprSig (HsType GhcPs) ~ (),+ FunReprCtx (HsType GhcPs) ~ HsType GhcPs,+ FunReprArg (HsType GhcPs) ~ HsType GhcPs,+ FunReprMult (HsType GhcPs) ~ HsType GhcPs,+ FunReprRet (HsType GhcPs) ~ HsType GhcPs+ )++hasDocs :: ParsedFunRepr' ig ctx arg mult ret -> Bool+hasDocs = \case+ ParsedFunSig {next} -> hasDocs next+ ParsedFunForall {next} -> hasDocs next+ ParsedFunQuals {next} -> hasDocs next+ ParsedFunArg {doc, next} -> isJust doc || hasDocs next+ ParsedFunReturn {doc} -> isJust doc++{----- Rendering ParsedFunRepr -----}++-- | For implementing function-arrows and related configuration, we'll collect+-- all the components of the function type first, then render as a block instead+-- of rendering each part independently, which will let us track local state+-- within a function type.+--+-- This function should be passed the first function-related construct we find;+-- see FunRepr for more details.+p_hsFun :: forall a. (FunRepr a, FunReprHsType) => a -> R ()+p_hsFun = p_hsFunParsed @a . parseFunRepr . L (noAnn @SrcSpanAnnA)++p_hsFunParsed :: forall a. (FunRepr a, FunReprHsType) => ParsedFunRepr a -> R ()+p_hsFunParsed = runPrintHsFun . p_hsFunParsed' @a++p_hsFunParsed' :: forall a. (FunRepr a, FunReprHsType) => ParsedFunRepr a -> PrintHsFun ()+p_hsFunParsed' = \case+ ParsedFunSig {sig, next} -> do+ -- Should only happen at the very beginning, if at all+ p_parsedFunSig @a sig next+ setMultilineContext next+ p_hsFunParsed' @a next+ ParsedFunForall {tele, next} -> do+ p_parsedFunForall tele+ setMultilineContext next+ p_hsFunParsed' @a next+ ParsedFunQuals {ctxs, next} -> do+ p_parsedFunQuals @a ctxs+ setMultilineContext next+ p_hsFunParsed' @a next+ ParsedFunArg {span, arg, doc, multAnn, next} -> do+ p_parsedFunArg @a span arg doc multAnn+ case next of+ ParsedFunArg {} -> pure ()+ _ -> do+ -- Don't reset multiline context within args; all args should be on+ -- one line together, or all on separate lines+ setMultilineContext next+ -- Reset inArgList, needed if there are multiple arg lists in one+ -- function type (e.g. `a -> Show a => b -> c`)+ setInArgList False+ p_hsFunParsed' @a next+ ParsedFunReturn {ret, doc} -> do+ p_parsedFunReturn @a ret doc++p_parsedFunSig :: forall a. (FunRepr a) => FunReprSig a -> ParsedFunRepr a -> PrintHsFun ()+p_parsedFunSig sig fun = do+ isTrailing <- getIsTrailing+ if isTrailing (Isn't #argDelim)+ then liftR $ do+ renderFunReprSig @a sig+ if hasDocs fun then newline else breakpoint+ else do+ liftR breakpoint+ setLeadingDelim (token'dcolon >> space)++p_parsedFunForall :: (FunReprHsType) => LocatedA (HsForAllTelescope GhcPs) -> PrintHsFun ()+p_parsedFunForall x@(L _ tele) = do+ setLocated_ x+ applyLeadingDelim+ vis <-+ case tele of+ HsForAllInvis _ bndrs -> do+ liftR $ p_forallBndrsStart p_hsTyVarBndr bndrs+ pure ForAllInvis+ HsForAllVis _ bndrs -> do+ liftR $ p_forallBndrsStart p_hsTyVarBndr bndrs+ pure ForAllVis++ isTrailing <- getIsTrailing+ isMultiline <- getIsMultiline+ if isTrailing (Isn't #argDelim) || not isMultiline+ then do+ liftR $ p_forallBndrsEnd (Without #extraSpace) vis+ interArgBreak+ else do+ interArgBreak+ let extraSpace = if isMultiline then With #extraSpace else Without #extraSpace+ setLeadingDelim (p_forallBndrsEnd extraSpace vis)++p_parsedFunQuals :: forall a. (FunRepr a) => [LocatedA (LocatedC [LocatedA (FunReprCtx a)])] -> PrintHsFun ()+p_parsedFunQuals ctxs = do+ isTrailing <- getIsTrailing+ -- we only want to set located on the first context+ case ctxs of+ ctx : _ -> setLocated_ ctx+ _ -> pure ()++ forM_ ctxs $ \(L _ lctx) -> do+ applyLeadingDelim+ liftR $ located lctx (p_hsContext' (renderFunReprCtx @a))+ if isTrailing (Isn't #argDelim)+ then do+ liftR $ space >> token'darrow+ interArgBreak+ else do+ interArgBreak+ setLeadingDelim (token'darrow >> space)++p_parsedFunArg ::+ forall a.+ (FunRepr a) =>+ SrcSpanAnnA ->+ LocatedA (FunReprArg a) ->+ Maybe (LHsDoc GhcPs) ->+ HsMultAnnOf (LocatedA (FunReprMult a)) GhcPs ->+ PrintHsFun ()+p_parsedFunArg span item doc multAnn = do+ isTrailing <- getIsTrailing++ -- We only want to set located on the first arg+ inArgList <- getInArgList+ when (not inArgList) $ do+ setLocated_ (L span item)+ setInArgList True++ let renderArrow = p_arrow (located' (renderFunReprMult @a)) multAnn+ withHaddocks (Isn't #end) doc $ do+ withApplyLeadingDelim $ \applyLeadingDelim' ->+ liftR . located item $ \arg -> do+ traverse_ id applyLeadingDelim'+ renderFunReprArg @a arg+ if isTrailing (Is #argDelim)+ then do+ liftR $ space >> renderArrow+ interArgBreak+ else do+ interArgBreak+ setLeadingDelim (renderArrow >> space)++p_parsedFunReturn ::+ forall a.+ (FunRepr a) =>+ LocatedA (FunReprRet a) ->+ Maybe (LHsDoc GhcPs) ->+ PrintHsFun ()+p_parsedFunReturn item doc = do+ setLocated_ item+ withHaddocks (Is #end) doc $ do+ applyLeadingDelim+ liftR $ located item (renderFunReprRet @a)++{----- forall -----}++data ForAllVisibility = ForAllInvis | ForAllVis++-- | Render several @forall@-ed variables.+p_forallBndrs ::+ (HasLoc l) =>+ ForAllVisibility ->+ (a -> R ()) ->+ [GenLocated l a] ->+ R ()+p_forallBndrs vis p tyvars = do+ p_forallBndrsStart p tyvars+ p_forallBndrsEnd (Without #extraSpace) vis++p_forallBndrsStart :: (HasLoc l) => (a -> R ()) -> [GenLocated l a] -> R ()+p_forallBndrsStart _ [] = token'forall+p_forallBndrsStart p tyvars = do+ switchLayout (locA <$> tyvars) $ do+ token'forall+ breakpoint+ inci $ do+ sitcc $ sep breakpoint (sitcc . located' p) tyvars++p_forallBndrsEnd :: Choice "extraSpace" -> ForAllVisibility -> R ()+p_forallBndrsEnd extraSpace = \case+ ForAllInvis -> txt dot >> space+ ForAllVis -> space >> token'rarrow+ where+ dot = if Choice.isTrue extraSpace then " ." else "."++{----- HsTyVarBndr -----}++class IsTyVarBndrFlag flag where+ isInferred :: flag -> Bool+ p_tyVarBndrFlag :: flag -> R ()+ p_tyVarBndrFlag _ = pure ()++instance IsTyVarBndrFlag () where+ isInferred () = False++instance IsTyVarBndrFlag Specificity where+ isInferred = \case+ InferredSpec -> True+ SpecifiedSpec -> False++instance IsTyVarBndrFlag (HsBndrVis GhcPs) where+ isInferred _ = False+ p_tyVarBndrFlag = \case+ HsBndrRequired NoExtField -> pure ()+ HsBndrInvisible _ -> txt "@"++p_hsTyVarBndr ::+ (IsTyVarBndrFlag flag, FunReprHsType) =>+ HsTyVarBndr flag GhcPs -> R ()+p_hsTyVarBndr HsTvb {..} = do+ p_tyVarBndrFlag tvb_flag+ let wrap+ | isInferred tvb_flag = braces N+ | otherwise = case tvb_kind of+ HsBndrKind {} -> parens N+ HsBndrNoKind {} -> id+ wrap $ do+ case tvb_var of+ HsBndrVar _ x -> p_rdrName x+ HsBndrWildCard _ -> txt "_"+ case tvb_kind of+ HsBndrKind _ k -> inci $ do+ p_hsFunParsed @(HsType GhcPs) $+ ParsedFunSig+ { sig = (),+ next = parseFunRepr k+ }+ HsBndrNoKind _ -> pure ()++{----- Contexts -----}++p_hsContext' ::+ (Outputable (GenLocated (Anno a) a), HasLoc (Anno a)) =>+ (a -> R ()) ->+ [XRec GhcPs a] ->+ R ()+p_hsContext' f = \case+ [] -> txt "()"+ [x] -> located x f+ xs -> do+ shouldSort <- getPrinterOpt poSortConstraints+ let sort = if shouldSort then sortOn showOutputable else id+ parens N $ sep commaDel (sitcc . located' f) (sort xs)++{----- Helpers -----}++withHaddocks :: (MonadR m) => Choice "end" -> Maybe (LHsDoc GhcPs) -> m a -> m a+withHaddocks isEnd doc m = do+ isTrailing <- getIsTrailing+ isLeadingHaddock <-+ liftR $+ getPrinterOpt poHaddockLocSignature <&> \case+ HaddockLocSigAuto ->+ if isTrailing (Is #argDelim) then With #pipe else Without #pipe+ HaddockLocSigLeading ->+ With #pipe+ HaddockLocSigTrailing ->+ Without #pipe++ if Choice.isTrue isLeadingHaddock+ then do+ traverse (liftR . p_hsDoc Pipe (With #endNewline)) doc *> m+ else do+ let (pre, endNewline) =+ if Choice.isTrue isEnd+ then (when (isJust doc) (liftR newline), Without #endNewline)+ else (pure (), With #endNewline)+ m <* pre <* traverse (liftR . p_hsDoc Caret endNewline) doc++interArgBreak :: PrintHsFun ()+interArgBreak = do+ isMultiline <- getIsMultiline+ liftR $ if isMultiline then newline else breakpoint
src/Ormolu/Printer/Operators.hs view
@@ -203,9 +203,9 @@ indices -> splitTree noptExprs noptOps indices where indicesOfHardSplitter =- fmap fst $- filter (isHardSplitterOp . opiFixityApproximation . snd) $- zip [0 ..] noptOps+ fmap fst+ $ filter (isHardSplitterOp . opiFixityApproximation . snd)+ $ zip [0 ..] noptOps indexOfMinMaxPrecOps [] = (Nothing, Nothing) indexOfMinMaxPrecOps (oo : oos) = go oos 1 oo (Just [0]) oo (Just [0]) where
src/Ormolu/Processing/Preprocess.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}@@ -12,6 +13,8 @@ import Data.Array as A import Data.Bifunctor (bimap) import Data.Char (isSpace)+import Data.Choice (Choice)+import Data.Choice qualified as Choice import Data.Function ((&)) import Data.IntMap (IntMap) import Data.IntMap.Strict qualified as IntMap@@ -29,13 +32,14 @@ -- and subregions to be formatted. preprocess :: -- | Whether CPP is enabled- Bool ->+ Choice "cppEnabled" -> RegionDeltas -> Text -> [Either Text RegionDeltas] preprocess cppEnabled region rawInput = rawSnippetsAndRegionsToFormat where- (linesNotToFormat', replacementLines) = linesNotToFormat cppEnabled region rawInput+ (linesNotToFormat', replacementLines) =+ linesNotToFormat cppEnabled region rawInput regionsToFormat = intSetToRegions rawLineLength $ IntSet.fromAscList [1 .. rawLineLength] IntSet.\\ linesNotToFormat'@@ -91,7 +95,7 @@ -- for specific lines. linesNotToFormat :: -- | Whether CPP is enabled- Bool ->+ Choice "cppEnabled" -> RegionDeltas -> Text -> (IntSet, IntMap Text)@@ -100,13 +104,16 @@ where unconsidered = IntSet.fromAscList $- [1 .. regionPrefixLength] <> [totalLines - regionSuffixLength + 1 .. totalLines]+ [1 .. regionPrefixLength]+ <> [totalLines - regionSuffixLength + 1 .. totalLines] totalLines = length (T.lines input) regionLines = linesInRegion region input (magicDisabled, lineUpdates) = magicDisabledLines regionLines otherDisabled = mconcat allLines regionLines where- allLines = [shebangLines, linePragmaLines] <> [cppLines | cppEnabled]+ allLines =+ [shebangLines, linePragmaLines]+ <> [cppLines | Choice.isTrue cppEnabled] -- | Ormolu state. data OrmoluState
src/Ormolu/Utils.hs view
@@ -73,14 +73,14 @@ -- | Split and normalize a doc string. The result is a list of lines that -- make up the comment.-splitDocString :: Bool -> HsDocString -> [Text]-splitDocString shouldEscapeCommentBraces docStr =+splitDocString :: HsDocString -> [Text]+splitDocString docStr = case r of [] -> [""] _ -> r where r =- fmap (escapeLeadingDollar . escapeCommentBraces)+ fmap escapeLeadingDollar . dropPaddingSpace' . dropWhileEnd T.null . fmap (T.stripEnd . T.pack)@@ -117,10 +117,6 @@ in if leadingSpace x then dropSpace <$> xs else xs- escapeCommentBraces =- if shouldEscapeCommentBraces- then T.replace "{-" "{\\-" . T.replace "-}" "-\\}"- else id -- | Increment line number in a 'SrcSpan'. incSpanLine :: Int -> SrcSpan -> SrcSpan
src/Ormolu/Utils/Glob.hs view
@@ -1,6 +1,7 @@ module Ormolu.Utils.Glob ( Glob, mkGlob,+ matchAllGlob, matchesGlob, ) where@@ -31,6 +32,9 @@ t -> let (m, t') = break (== '*') t in MatchExactly m : parsePart t'++matchAllGlob :: Glob+matchAllGlob = Glob [DoubleWildcard] matchesGlob :: String -> Glob -> Bool matchesGlob s (Glob ps) = s `matchesGlobParts` ps
src/Ormolu/Utils/IO.hs view
@@ -16,7 +16,7 @@ import Data.Map.Lazy qualified as M import System.Directory import System.FilePath-import System.IO.Error (isDoesNotExistError)+import System.IO.Error (isDoesNotExistError, isPermissionError) -- | Find the path to the closest file higher in the file hierarchy that -- satisfies a given predicate.@@ -30,25 +30,36 @@ m (Maybe FilePath) findClosestFileSatisfying isRightFile rootOfSearch = liftIO $ do parentDir <- takeDirectory <$> makeAbsolute rootOfSearch- dirEntries <-- listDirectory parentDir `catch` \case- (isDoesNotExistError -> True) -> pure []+ maybeDirEntries <-+ (Just <$> listDirectory parentDir) `catch` \case+ -- The directory does not exist. This is expected: the search may start+ -- from a path that does not exist yet (e.g. a file about to be created),+ -- whose absolute form still lies below existing parent directories.+ -- Treat it as empty and keep searching upwards.+ (isDoesNotExistError -> True) -> pure (Just [])+ -- We lack the permissions to read the directory, e.g. when running in a+ -- sandbox that restricts access to parent directories. Abort the search:+ -- we almost certainly cannot read any parent directory either.+ (isPermissionError -> True) -> pure Nothing e -> throwIO e- let searchAtParentDirLevel = \case- [] -> pure Nothing- x : xs ->- if isRightFile x- then- doesFileExist (parentDir </> x) >>= \case- True -> pure (Just x)- False -> searchAtParentDirLevel xs- else searchAtParentDirLevel xs- searchAtParentDirLevel dirEntries >>= \case- Just foundFile -> pure . Just $ parentDir </> foundFile- Nothing ->- if isDrive parentDir- then pure Nothing- else findClosestFileSatisfying isRightFile parentDir+ case maybeDirEntries of+ Nothing -> pure Nothing+ Just entries -> do+ let searchAtParentDirLevel = \case+ [] -> pure Nothing+ x : xs ->+ if isRightFile x+ then+ doesFileExist (parentDir </> x) >>= \case+ True -> pure (Just x)+ False -> searchAtParentDirLevel xs+ else searchAtParentDirLevel xs+ searchAtParentDirLevel entries >>= \case+ Just foundFile -> pure . Just $ parentDir </> foundFile+ Nothing ->+ if isDrive parentDir+ then pure Nothing+ else findClosestFileSatisfying isRightFile parentDir newtype Cache k v = Cache (IORef (Map k v))
tests/Ormolu/Config/PrinterOptsSpec.hs view
@@ -13,6 +13,7 @@ import Control.Monad (forM_, when) import Data.Algorithm.DiffContext qualified as Diff import Data.Char (isSpace)+import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty qualified as NonEmpty import Data.Maybe (isJust) import Data.Set qualified as S@@ -26,7 +27,7 @@ import Ormolu.Config import Ormolu.Exception (OrmoluException, printOrmoluException) import Ormolu.Terminal (runTerm)-import Ormolu.Utils.Glob (mkGlob)+import Ormolu.Utils.Glob (matchAllGlob, mkGlob) import Path ( File, Path,@@ -111,6 +112,24 @@ checkIdempotence = True }, TestGroup+ { label = "record-style",+ isMulti = False,+ testCases = liftA2 (,) allOptions allOptions,+ updateConfig = \(recordStyle, commaStyle) opts ->+ opts+ { poRecordStyle = pure recordStyle,+ -- Test record style in combination with comma style+ poCommaStyle = pure commaStyle,+ -- Makes the indentation more visible+ poIndentation = pure 4+ },+ showTestCase = \(recordStyle, commaStyle) ->+ [ renderPrinterOpt recordStyle,+ renderPrinterOpt commaStyle+ ],+ checkIdempotence = True+ },+ TestGroup { label = "import-export", isMulti = True, testCases = allOptions,@@ -130,62 +149,7 @@ ImportGroupByQualified, ImportGroupByScope, ImportGroupByScopeThenQualified,- ImportGroupCustom . NonEmpty.fromList $- [ ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "Data.Text"),- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = defaultImportRulePriority- }- ]- },- ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchAllModules,- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = ImportRulePriority 100- }- ]- },- ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "SomeInternal.**"),- igrQualifiedMatcher = MatchQualifiedOnly,- igrPriority = defaultImportRulePriority- },- ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "Unknown.**"),- igrQualifiedMatcher = MatchUnqualifiedOnly,- igrPriority = defaultImportRulePriority- }- ]- },- ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchLocalModules,- igrQualifiedMatcher = MatchUnqualifiedOnly,- igrPriority = defaultImportRulePriority- },- ImportGroupRule- { igrModuleMatcher = MatchAllModules,- igrQualifiedMatcher = MatchQualifiedOnly,- igrPriority = defaultImportRulePriority- }- ]- }- ]+ ImportGroupCustom importGroupCustomRules ], updateConfig = \igs opts -> opts@@ -446,7 +410,7 @@ Nothing -> expectationFailure "Output does not exist. Try running with ORMOLU_REGENERATE_EXAMPLES=1" Just expected ->- when (actual /= expected) $+ when (actual /= expected) $ do expectationFailure . T.unpack $ getDiff ("actual", actual) ("expected", expected) @@ -496,9 +460,9 @@ getDiff :: (String, Text) -> (String, Text) -> Text getDiff (s1Name, s1) (s2Name, s2) =- T.pack . Doc.render $- Diff.prettyContextDiff (Doc.text s1Name) (Doc.text s2Name) (Doc.text . T.unpack . Diff.unnumber) $- Diff.getContextDiff (Just 2) (T.lines s1) (T.lines s2)+ T.pack . Doc.render+ $ Diff.prettyContextDiff (Doc.text s1Name) (Doc.text s2Name) (Doc.text . T.unpack . Diff.unnumber)+ $ Diff.getContextDiff (Just 2) (T.lines s1) (T.lines s2) renderOrmoluException :: OrmoluException -> IO String renderOrmoluException e =@@ -525,3 +489,58 @@ spanEnd f xs = let xs' = reverse xs in (reverse $ dropWhile f xs', reverse $ takeWhile f xs')++importGroupCustomRules :: NonEmpty ImportGroup+importGroupCustomRules =+ NonEmpty.fromList+ [ defaultImportGroup . NonEmpty.fromList $+ [ defaultImportGroupRule+ { igrImportListMatcher = MatchWholeModuleImport,+ igrQualifiedMatcher = MatchUnqualifiedOnly+ }+ ],+ defaultImportGroup . NonEmpty.fromList $+ [ defaultImportGroupRule {igrGlob = mkGlob "Data.Text"}+ ],+ defaultImportGroup . NonEmpty.fromList $+ [ defaultImportGroupRule+ { igrPriority = ImportRulePriority 100+ }+ ],+ defaultImportGroup . NonEmpty.fromList $+ [ defaultImportGroupRule+ { igrGlob = mkGlob "SomeInternal.**",+ igrQualifiedMatcher = MatchQualifiedOnly+ },+ defaultImportGroupRule+ { igrGlob = mkGlob "Unknown.**",+ igrQualifiedMatcher = MatchUnqualifiedOnly+ }+ ],+ defaultImportGroup . NonEmpty.fromList $+ [ defaultImportGroupRule+ { igrQualifiedMatcher = MatchUnqualifiedOnly,+ igrScopeMatcher = MatchLocalModules+ },+ defaultImportGroupRule+ { igrQualifiedMatcher = MatchQualifiedOnly+ }+ ]+ ]+ where+ defaultImportGroup :: NonEmpty ImportGroupRule -> ImportGroup+ defaultImportGroup rules =+ ImportGroup+ { igName = Nothing,+ igRules = rules+ }++ defaultImportGroupRule :: ImportGroupRule+ defaultImportGroupRule =+ ImportGroupRule+ { igrGlob = matchAllGlob,+ igrImportListMatcher = MatchAnyImportDeclaration,+ igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,+ igrScopeMatcher = MatchAllModules,+ igrPriority = defaultImportRulePriority+ }
tests/Ormolu/ConfigSpec.hs view
@@ -3,14 +3,16 @@ -- | Tests for Fourmolu file configuration. module Ormolu.ConfigSpec (spec) where +import Control.Monad ((>=>)) import Data.ByteString.Char8 qualified as Char8 import Data.List (isInfixOf)+import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NonEmpty import Data.Map qualified as Map import Data.Yaml qualified as Yaml-import Ormolu.Config (FourmoluConfig (..), ImportGroup (..), ImportGroupRule (..), ImportGrouping (..), ImportModuleMatcher (..), ImportRulePriority (ImportRulePriority), PrinterOpts (..), QualifiedImportMatcher (MatchBothQualifiedAndUnqualified, MatchQualifiedOnly, MatchUnqualifiedOnly), defaultImportRulePriority, matchAllRulePriority, resolvePrinterOpts)+import Ormolu.Config import Ormolu.Fixity (ModuleReexports (..))-import Ormolu.Utils.Glob (mkGlob)+import Ormolu.Utils.Glob (matchAllGlob, mkGlob) import Test.Hspec spec :: Spec@@ -71,165 +73,84 @@ [ "import-grouping:", " - name: Some name", " rules:",- " - match: all"+ " - {}" ] let actualStrategy = poImportGrouping (cfgFilePrinterOpts config) expectedRules = NonEmpty.fromList [ ImportGroup { igName = Just "Some name",- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchAllModules,- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = matchAllRulePriority- }- ]- }- ]- actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)- it "enables the 'qualified' rule option" $ do- config <-- Yaml.decodeThrow . Char8.pack . unlines $- [ "import-grouping:",- " - rules:",- " - glob: \"**\"",- " qualified: yes"- ]- let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)- expectedRules =- NonEmpty.fromList- [ ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "**"),- igrQualifiedMatcher = MatchQualifiedOnly,- igrPriority = defaultImportRulePriority- }- ]- }- ]- actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)- it "disables the 'qualified' rule option" $ do- config <-- Yaml.decodeThrow . Char8.pack . unlines $- [ "import-grouping:",- " - rules:",- " - glob: \"**\"",- " qualified: no"- ]- let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)- expectedRules =- NonEmpty.fromList- [ ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "**"),- igrQualifiedMatcher = MatchUnqualifiedOnly,- igrPriority = defaultImportRulePriority- }- ]- }- ]- actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)- it "decodes the 'priority' rule option" $ do- config <-- Yaml.decodeThrow . Char8.pack . unlines $- [ "import-grouping:",- " - rules:",- " - match: all",- " priority: 55"- ]- let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)- expectedRules =- NonEmpty.fromList- [ ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchAllModules,- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = ImportRulePriority 55- }- ]- }- ]- actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)- it "parses a 'glob' rule" $ do- config <-- Yaml.decodeThrow . Char8.pack . unlines $- [ "import-grouping:",- " - rules:",- " - glob: Data.Text.**"- ]- let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)- expectedRules =- NonEmpty.fromList- [ ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "Data.Text.**"),- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = defaultImportRulePriority- }- ]- }- ]- actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)- it "parses a 'match' rule for local modules" $ do- config <-- Yaml.decodeThrow . Char8.pack . unlines $- [ "import-grouping:",- " - rules:",- " - match: local-modules"- ]- let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)- expectedRules =- NonEmpty.fromList- [ ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchLocalModules,- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = defaultImportRulePriority- }- ]- }- ]- actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)- it "parses a 'match' rule for all modules" $ do- config <-- Yaml.decodeThrow . Char8.pack . unlines $- [ "import-grouping:",- " - rules:",- " - match: all"- ]- let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)- expectedRules =- NonEmpty.fromList- [ ImportGroup- { igName = Nothing,- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchAllModules,- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = matchAllRulePriority- }- ]+ igRules = NonEmpty.fromList [defaultImportGroupRule] } ] actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)++ let checkRule :: (HasCallStack) => String -> String -> ImportGroupRule -> SpecWith ()+ checkRule desc yamlRuleConfig expectedRuleConfig =+ it ("parses " ++ desc) $ do+ let yamlConfig =+ unlines+ [ "import-grouping:",+ " - rules:",+ " - " ++ yamlRuleConfig+ ]++ downCustomRules (ImportGroupCustom customRules) = Just customRules+ downCustomRules _ = Nothing+ downSingleton (a :| []) = Just a+ downSingleton _ = Nothing+ accessTestedRule = downCustomRules >=> downSingleton >=> downSingleton . igRules++ config <- Yaml.decodeThrow (Char8.pack yamlConfig)++ let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)+ case actualStrategy >>= accessTestedRule of+ Nothing -> expectationFailure "A single tested rule change was expected"+ Just actualRule -> actualRule `shouldBe` expectedRuleConfig++ checkRuleAttribute :: (HasCallStack) => String -> String -> (ImportGroupRule -> ImportGroupRule) -> SpecWith ()+ checkRuleAttribute desc yamlRuleConfig applyExpectedChange = do+ let baseArbitraryYamlConfig = "glob: \"**\""+ yamlConfig = "{ " ++ baseArbitraryYamlConfig ++ ", " ++ yamlRuleConfig ++ "}"++ baseArbitraryConfig = defaultImportGroupRule {igrGlob = matchAllGlob}+ expectedRuleConfig = applyExpectedChange baseArbitraryConfig+ checkRule+ ("rules with the " ++ desc ++ " rule option")+ yamlConfig+ expectedRuleConfig++ checkRule "the 'glob' rule" "glob: Data.Text.**" defaultImportGroupRule {igrGlob = mkGlob "Data.Text.**"}+ checkRule "the default 'glob' rule (match all modules)" "{}" defaultImportGroupRule {igrGlob = mkGlob "**"}++ checkRuleAttribute "'any' import list" "import-list: any" $ \config -> config {igrImportListMatcher = MatchAnyImportDeclaration}+ checkRuleAttribute "'explicit' import list" "import-list: explicit" $ \config -> config {igrImportListMatcher = MatchExplicitImportList}+ checkRuleAttribute "'hiding' import list" "import-list: hiding" $ \config -> config {igrImportListMatcher = MatchHidingImportClause}+ checkRuleAttribute "'none' import list" "import-list: none" $ \config -> config {igrImportListMatcher = MatchWholeModuleImport}+ checkRuleAttribute "default import list" "" $ \config -> config {igrImportListMatcher = MatchAnyImportDeclaration}++ checkRuleAttribute "'qualified: yes'" "qualified: yes" $ \config -> config {igrQualifiedMatcher = MatchQualifiedOnly}+ checkRuleAttribute "'qualified: no'" "qualified: no" $ \config -> config {igrQualifiedMatcher = MatchUnqualifiedOnly}+ checkRuleAttribute "default 'qualified' (match any)" "" $ \config -> config {igrQualifiedMatcher = MatchBothQualifiedAndUnqualified}++ checkRuleAttribute "'scope: any'" "scope: any" $ \config -> config {igrScopeMatcher = MatchAllModules}+ checkRuleAttribute "'scope: local'" "scope: local" $ \config -> config {igrScopeMatcher = MatchLocalModules}+ checkRuleAttribute "'scope: external'" "scope: external" $ \config -> config {igrScopeMatcher = MatchExternalModules}+ checkRuleAttribute "default 'scope' (match all)" "" $ \config -> config {igrScopeMatcher = MatchAllModules}++ checkRuleAttribute "'priority' (55 priority example)" "priority: 55" $ \config -> config {igrPriority = ImportRulePriority 55}+ checkRuleAttribute "'priority' (80 priority example)" "priority: 80" $ \config -> config {igrPriority = ImportRulePriority 80}+ checkRuleAttribute "default 'priority' (50)" "" $ \config -> config {igrPriority = ImportRulePriority 50}++ checkRule+ "the legacy 'match' rule for all modules (with a 100 priority)"+ "match: all"+ defaultImportGroupRule {igrGlob = mkGlob "**", igrScopeMatcher = MatchAllModules, igrPriority = ImportRulePriority 100}+ checkRule+ "the legacy 'match' rule for local modules"+ "match: local-modules"+ defaultImportGroupRule {igrGlob = mkGlob "**", igrScopeMatcher = MatchLocalModules, igrPriority = ImportRulePriority 60}+ it "parses multiple group configurations and rules in their order of appearance in the configuration" $ do config <- Yaml.decodeThrow . Char8.pack . unlines $@@ -240,7 +161,6 @@ " - name: The rest", " rules:", " - match: all",- " priority: 100", " - name: My internals and monads unqualified", " rules:", " - match: local-modules",@@ -262,68 +182,31 @@ NonEmpty.fromList [ ImportGroup { igName = Just "Text modules",- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "Data.Text"),- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = defaultImportRulePriority- }- ]+ igRules = NonEmpty.fromList [defaultImportGroupRule {igrGlob = mkGlob "Data.Text"}] }, ImportGroup { igName = Just "The rest",- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchAllModules,- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = ImportRulePriority 100- }- ]+ igRules = NonEmpty.fromList [defaultImportGroupRule {igrGlob = matchAllGlob, igrPriority = ImportRulePriority 100}] }, ImportGroup { igName = Just "My internals and monads unqualified", igRules = NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchLocalModules,- igrQualifiedMatcher = MatchUnqualifiedOnly,- igrPriority = defaultImportRulePriority- },- ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "Control.Monad"),- igrQualifiedMatcher = MatchUnqualifiedOnly,- igrPriority = defaultImportRulePriority- }+ [ defaultImportGroupRule {igrQualifiedMatcher = MatchUnqualifiedOnly, igrScopeMatcher = MatchLocalModules, igrPriority = ImportRulePriority 60},+ defaultImportGroupRule {igrGlob = mkGlob "Control.Monad", igrQualifiedMatcher = MatchUnqualifiedOnly} ] }, ImportGroup { igName = Just "My internals and monads qualified", igRules = NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchLocalModules,- igrQualifiedMatcher = MatchQualifiedOnly,- igrPriority = defaultImportRulePriority- },- ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "Control.Monad"),- igrQualifiedMatcher = MatchQualifiedOnly,- igrPriority = defaultImportRulePriority- }+ [ defaultImportGroupRule {igrQualifiedMatcher = MatchQualifiedOnly, igrScopeMatcher = MatchLocalModules, igrPriority = ImportRulePriority 60},+ defaultImportGroupRule {igrGlob = mkGlob "Control.Monad", igrQualifiedMatcher = MatchQualifiedOnly} ] }, ImportGroup { igName = Just "Specific monads",- igRules =- NonEmpty.fromList- [ ImportGroupRule- { igrModuleMatcher = MatchGlob (mkGlob "Control.Monad.**"),- igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,- igrPriority = defaultImportRulePriority- }- ]+ igRules = NonEmpty.fromList [defaultImportGroupRule {igrGlob = mkGlob "Control.Monad.**"}] } ] actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)@@ -332,9 +215,19 @@ Yaml.decodeEither' @FourmoluConfig . Char8.pack . unlines $ [ "import-grouping:", " - rules:",- " - some-unknown-rule-type: whatever"+ " - match: whatever" ] isAnUnknownModuleMatcher e = case e of- Left (Yaml.AesonException msg) -> "Unknown or invalid module matcher" `isInfixOf` msg+ Left (Yaml.AesonException msg) -> "Unknown legacy match value" `isInfixOf` msg _ -> False decodeResult `shouldSatisfy` isAnUnknownModuleMatcher++defaultImportGroupRule :: ImportGroupRule+defaultImportGroupRule =+ ImportGroupRule+ { igrGlob = matchAllGlob,+ igrImportListMatcher = MatchAnyImportDeclaration,+ igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,+ igrScopeMatcher = MatchAllModules,+ igrPriority = defaultImportRulePriority+ }
tests/Ormolu/Fixity/ParserSpec.hs view
@@ -214,7 +214,7 @@ elabel "module name" ] )- it "fails with correct parse error (typo: export intead exports)" $+ it "fails with correct parse error (typo: export instead exports)" $ parseModuleReexportDeclaration "module Control.Lens export Control.Lens.Lens" `shouldFailWith` err 20
tests/Ormolu/FixitySpec.hs view
@@ -201,44 +201,32 @@ ["esqueleto"] [package_ "bob" $ import_ "Database.Esqueleto.Experimental"] [(unqual "++.", defaultFixityApproximation)]- it "default module re-exports: Control.Lens brings into scope Control.Lens.Lens" $+ it "re-exports baked into the database: Control.Lens brings <+~ into scope" $ checkFixities ["lens"]- ( applyModuleReexports- defaultModuleReexports- [import_ "Control.Lens"]- )+ [import_ "Control.Lens"] [(unqual "<+~", FixityApproximation (Just InfixR) 4 4)]- it "default module re-exports: Control.Lens qualified brings into scope Control.Lens.Lens" $+ it "re-exports baked into the database: Control.Lens qualified" $ checkFixities ["lens"]- ( applyModuleReexports- defaultModuleReexports- [import_ "Control.Lens" & qualified_]- )+ [import_ "Control.Lens" & qualified_] [ (unqual "<+~", defaultFixityApproximation),- (qual "Control.Lens.Lens" "<+~", defaultFixityApproximation), (qual "Control.Lens" "<+~", FixityApproximation (Just InfixR) 4 4) ]- it "default module re-exports: Control.Lens qualified as brings into scope Control.Lens.Lens" $+ it "re-exports baked into the database: Control.Lens qualified as" $ checkFixities ["lens"]- ( applyModuleReexports- defaultModuleReexports- [import_ "Control.Lens" & qualified_ & as_ "L"]- )+ [import_ "Control.Lens" & qualified_ & as_ "L"] [ (unqual "<+~", defaultFixityApproximation),- (qual "Control.Lens.Lens" "<+~", defaultFixityApproximation), (qual "Control.Lens" "<+~", defaultFixityApproximation), (qual "L" "<+~", FixityApproximation (Just InfixR) 4 4) ] it "re-export chains: exported module can itself re-export another module" $ do let reexports = ModuleReexports $- Map.insert+ Map.singleton "Foo" ((Nothing, "Control.Lens") :| [])- (unModuleReexports defaultModuleReexports) checkFixities ["lens"] ( applyModuleReexports
tests/Ormolu/Integration/Utils.hs view
@@ -10,15 +10,16 @@ where import Control.Monad (when)-import System.Directory (findExecutable)+import System.Directory (findExecutable, makeAbsolute) import System.Environment (getEnvironment) import System.Exit (ExitCode (..)) import System.Process (CreateProcess (..), readCreateProcessWithExitCode) import System.Process qualified as Process -- | Find a `fourmolu` executable on PATH.+-- Needs to be absolute to work around https://github.com/haskell/cabal/issues/11598 getFourmoluExe :: IO FilePath-getFourmoluExe = findExecutable "fourmolu" >>= maybe (fail "Could not find fourmolu executable") return+getFourmoluExe = findExecutable "fourmolu" >>= maybe (fail "Could not find fourmolu executable") makeAbsolute -- | Like 'System.Process.readProcess', except without specifying stdin and -- with better failure messages.
tests/Ormolu/PrinterSpec.hs view
@@ -64,6 +64,10 @@ "esqueleto", "hspec", "lens",+ "megaparsec",+ "optics",+ "relude",+ "rio", "servant" ] }