├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── .tidyrc.json ├── LICENSE ├── README.md ├── bin ├── index.dev.js ├── index.js ├── spago.yaml └── src │ ├── Bin │ ├── FormatOptions.purs │ ├── Timing.js │ ├── Timing.purs │ ├── Version.js │ ├── Version.purs │ └── Worker.purs │ └── Main.purs ├── package-lock.json ├── package.json ├── script ├── spago.yaml └── src │ ├── GenerateDefaultOperatorsModule.js │ └── GenerateDefaultOperatorsModule.purs ├── spago.lock ├── spago.yaml ├── src ├── Tidy.purs └── Tidy │ ├── Doc.purs │ ├── Hang.purs │ ├── Operators.purs │ ├── Operators │ └── Defaults.purs │ ├── Precedence.purs │ ├── Token.purs │ └── Util.purs └── test ├── FormatDirective.purs ├── Main.purs ├── Snapshot.purs └── snapshots ├── .gitattributes ├── Array.input ├── Array.output ├── BlockComments.input ├── BlockComments.output ├── DataDeclarations.input ├── DataDeclarations.output ├── DeclarationBreaks.input ├── DeclarationBreaks.output ├── DeclarationSignatures.input ├── DeclarationSignatures.output ├── DelimitersWithLeadingComments.input ├── DelimitersWithLeadingComments.output ├── Exports.input ├── Exports.output ├── ForeignImportSignatures.input ├── ForeignImportSignatures.output ├── Guards.input ├── Guards.output ├── HTML.input ├── HTML.output ├── IfThenElse.input ├── IfThenElse.output ├── ImportSortIde.input ├── ImportSortIde.output ├── ImportWrap.input ├── ImportWrap.output ├── Imports.input ├── Imports.output ├── Instance.input ├── Instance.output ├── InstanceChain.input ├── InstanceChain.output ├── ModuleHeader.input ├── ModuleHeader.output ├── MultiCase.input ├── MultiCase.output ├── MultilineApplications.input ├── MultilineApplications.output ├── MultilineBindings.input ├── MultilineBindings.output ├── MultilineNamedBinders.input ├── MultilineNamedBinders.output ├── MultilineOperatorArguments.input ├── MultilineOperatorArguments.output ├── MultilineStringLiterals.input ├── MultilineStringLiterals.output ├── MultilineSuperClasses.input ├── MultilineSuperClasses.output ├── OperatorsReversed.input ├── OperatorsReversed.output ├── Shebang.input ├── Shebang.output ├── TrailingLineComments.input ├── TrailingLineComments.output ├── UnicodeSignatures.input ├── UnicodeSignatures.output ├── UnicodeSuperclass.input ├── UnicodeSuperclass.output ├── UnusualLineComments.input ├── UnusualLineComments.output ├── VisibleTypeApplications.input └── VisibleTypeApplications.output /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: 20 17 | cache: 'npm' 18 | 19 | - name: Cache PureScript dependencies 20 | uses: actions/cache@v4 21 | with: 22 | key: ${{ runner.os }}-spago-${{ hashFiles('**/spago.lock') }} 23 | path: | 24 | .spago 25 | output 26 | 27 | - name: Install npm dependencies 28 | run: npm install 29 | 30 | - name: Build source 31 | run: npm run bundle -- --pedantic-packages 32 | 33 | - name: Run tests 34 | run: npm run test -- --offline --quiet 35 | 36 | - name: Verify formatting 37 | run: npm run check-self 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bower_components/ 2 | /node_modules/ 3 | /.pulp-cache/ 4 | /output/ 5 | /output-es/ 6 | /bundle/ 7 | /generated-docs/ 8 | /.psc-package/ 9 | /.psc* 10 | /.purs* 11 | /.psa* 12 | /.spago 13 | /*.purs* 14 | /.vscode 15 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="bin-v" 2 | -------------------------------------------------------------------------------- /.tidyrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "importSort": "ide", 3 | "importWrap": "source", 4 | "indent": 2, 5 | "operatorsFile": null, 6 | "ribbon": 1, 7 | "typeArrowPlacement": "first", 8 | "unicode": "never", 9 | "width": null 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Nathan Faubion 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # purescript-tidy 2 | 3 | A syntax tidy-upper (formatter) for PureScript. 4 | 5 | ## Install 6 | 7 | ```console 8 | $ npm install -g purs-tidy 9 | ``` 10 | 11 | Also available for [Nix](https://nixos.org/) via [Nixpkgs 22.11+](https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=purs-tidy) and [Easy PureScript Nix](https://github.com/justinwoo/easy-purescript-nix) 12 | 13 | ## Usage 14 | 15 | You can use `purs-tidy` to format files in place or via STDIN / STDOUT (which is useful for editor integration): 16 | 17 | ##### Formatting a collection of files in place: 18 | 19 | ```console 20 | $ purs-tidy format-in-place "src/**/*.purs" 21 | ``` 22 | 23 | ##### Using STDIN to format a file: 24 | 25 | ```console 26 | $ purs-tidy format < MyFile.purs 27 | ``` 28 | 29 | You can also use `purs-tidy` to verify whether files have already been formatted. This is often useful to verify, in continuous integration, that all project files are formatted according to the configuration. Files that would be changed by running `format-in-place` are listed out. 30 | 31 | 32 | ##### Verifying files are formatted 33 | 34 | ```console 35 | $ purs-tidy check "src/**/*.purs" 36 | All files are formatted. 37 | ``` 38 | 39 | ### Configuration 40 | 41 | You can see all configuration that `purs-tidy` accepts using the `--help` flag for the command you are using: 42 | 43 | ```console 44 | $ purs-tidy format-in-place --help 45 | ``` 46 | 47 | Some common options include: 48 | 49 | - `--indent` to set the number of spaces used in indentation, which defaults to 2 spaces 50 | - `--arrow-first` or `--arrow-last` to control whether type signatures put arrows first on the line or last on the line (purty-style), which defaults to arrow-first. 51 | 52 | You can generate a `.tidyrc.json` using the `generate-config` command. If a `.tidyrc.json` file is found, it will be used in lieu of CLI arguments. 53 | 54 | ### Operator Precedence 55 | 56 | To support correct operator precedence without having to parse your entire 57 | source tree (potentially for a single file), `purs-tidy` uses a pre-baked 58 | operator precedence table. By default, `purs-tidy` ships with a table built 59 | from the core and contrib organizations. If you need support for more 60 | operators, you can generate your own table using the `generate-operators` 61 | command. 62 | 63 | ```console 64 | $ spago sources | xargs purs-tidy generate-operators > .tidyoperators 65 | $ purs-tidy generate-config --arrow-first --unicode-never --operators .tidyoperators 66 | ``` 67 | 68 | ## Editor Support 69 | 70 | * [Spacemacs](#spacemacs) 71 | * [Vim](#vim) 72 | * [VS Code](#vs-code) 73 | 74 | ### Spacemacs 75 | 76 | [Spacemacs' Purescript layer](https://github.com/syl20bnr/spacemacs/tree/develop/layers/%2Blang/purescript) 77 | supports formatting using purs-tidy out of the box. 78 | 79 | You can run the formatter manually with either `M-x spacemacs/purescript-format` or with the shortcut `SPC m =`. 80 | 81 | To enable automatic formatting of the buffer on save, enable `purescript-fmt-on-save` in your spacemacs config: 82 | 83 | ```elisp 84 | (setq-default dotspacemacs-configuration-layers '( 85 | (purescript :variables 86 | purescript-fmt-on-save t))) 87 | ``` 88 | 89 | 90 | ### Vim 91 | 92 | #### via [ALE](https://github.com/dense-analysis/ale) 93 | 94 | Add to your other fixers `.vimrc` or `$XDG_CONFIG_HOME/neovim/init.vim` 95 | 96 | ```viml 97 | let b:ale_fixers = { 'purescript': [ 'purstidy' ] } 98 | " suggested to fix on save 99 | let g:ale_fix_on_save = 1 100 | ``` 101 | 102 | #### via [Neoformat](https://github.com/sbdchd/neoformat) 103 | 104 | Add to your `.vimrc` or `$XDG_CONFIG_HOME/neovim/init.vim` 105 | 106 | ```viml 107 | let g:neoformat_enabled_purescript = ['purstidy'] 108 | ``` 109 | 110 | ### VS Code 111 | 112 | #### via [PureScript IDE](https://marketplace.visualstudio.com/items?itemName=nwolverson.ide-purescript) 113 | 114 | The PureScript IDE plugin for VS Code supports `purs-tidy` as a built-in formatter in versions after `0.25.1`. Choose `purs-tidy` from the list of supported formatters in the settings, or add this to your `settings.json`: 115 | 116 | ```json 117 | "purescript.formatter": "purs-tidy" 118 | ``` 119 | 120 | ## Development 121 | 122 | ### Requirements 123 | 124 | * `purs`: 0.15 125 | * `spago`: 0.20 126 | * `node`: 14 127 | * `esbuild`: 0.14 128 | 129 | ### Running `bin` 130 | 131 | For local development pointing to the `output` directory: 132 | 133 | ```console 134 | $ npm run build 135 | $ ./bin/index.dev.js --help 136 | ``` 137 | 138 | For a local production build pointing to the `bundle` directory: 139 | 140 | ```console 141 | $ npm run bundle 142 | $ ./bin/index.js --help 143 | ``` 144 | 145 | If you would like to use your local build of `purs-tidy` in your editor, use path to `bin/index.js` instead of the `purs-tidy` binary in your settings. For example, instead of setting the format command to `purs-tidy format`, set it to `$TIDY_DIR/bin/index.js format` where `$TIDY_DIR` is the location of your checkout of this repository. 146 | 147 | ### Running `test` 148 | 149 | To accept snapshot tests: 150 | 151 | ```console 152 | $ npm run test -- -a "--accept" 153 | ``` 154 | 155 | ### Generating the built-in operator table 156 | 157 | ```console 158 | $ npm run generate-default-operators 159 | ``` 160 | -------------------------------------------------------------------------------- /bin/index.dev.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --experimental-json-modules 2 | import url from "url"; 3 | import { main } from "../output/Main/index.js"; 4 | 5 | process.env["TIDY_INSTALL_LOC"] = url.fileURLToPath(new URL('..', import.meta.url)); 6 | 7 | main(); 8 | -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import url from "url"; 3 | import { main } from "../bundle/Main/index.js"; 4 | 5 | process.env["TIDY_INSTALL_LOC"] = url.fileURLToPath(new URL('..', import.meta.url)); 6 | 7 | main(); 8 | -------------------------------------------------------------------------------- /bin/spago.yaml: -------------------------------------------------------------------------------- 1 | package: 2 | name: tidy-bin 3 | build: 4 | strict: true 5 | dependencies: 6 | - aff: ">=8.0.0 <9.0.0" 7 | - argonaut-codecs: ">=9.1.0 <10.0.0" 8 | - argonaut-core: ">=7.0.0 <8.0.0" 9 | - argparse-basic: ">=2.0.0 <3.0.0" 10 | - arrays: ">=7.3.0 <8.0.0" 11 | - console: ">=6.1.0 <7.0.0" 12 | - control: ">=6.0.0 <7.0.0" 13 | - datetime: ">=6.1.0 <7.0.0" 14 | - dodo-printer: ">=2.2.3 <3.0.0" 15 | - effect: ">=4.0.0 <5.0.0" 16 | - either: ">=6.1.0 <7.0.0" 17 | - foldable-traversable: ">=6.0.0 <7.0.0" 18 | - foreign-object: ">=4.1.0 <5.0.0" 19 | - language-cst-parser: ">=0.14.1 <0.15.0" 20 | - lazy: ">=6.0.0 <7.0.0" 21 | - lists: ">=7.0.0 <8.0.0" 22 | - maybe: ">=6.0.0 <7.0.0" 23 | - newtype: ">=5.0.0 <6.0.0" 24 | - node-buffer: ">=9.0.0 <10.0.0" 25 | - node-event-emitter: ">=3.0.0 <4.0.0" 26 | - node-fs: ">=9.2.0 <10.0.0" 27 | - node-glob-basic: ">=2.0.0 <3.0.0" 28 | - node-path: ">=5.0.1 <6.0.0" 29 | - node-process: ">=11.2.0 <12.0.0" 30 | - node-streams: ">=9.0.1 <10.0.0" 31 | - node-workerbees: ">=0.3.1 <0.4.0" 32 | - numbers: ">=9.0.1 <10.0.0" 33 | - ordered-collections: ">=3.2.0 <4.0.0" 34 | - parallel: ">=7.0.0 <8.0.0" 35 | - partial: ">=4.0.0 <5.0.0" 36 | - prelude: ">=6.0.1 <7.0.0" 37 | - refs: ">=6.0.0 <7.0.0" 38 | - strings: ">=6.0.1 <7.0.0" 39 | - tidy: "*" 40 | - transformers: ">=6.1.0 <7.0.0" 41 | - tuples: ">=7.0.0 <8.0.0" 42 | -------------------------------------------------------------------------------- /bin/src/Bin/FormatOptions.purs: -------------------------------------------------------------------------------- 1 | module Bin.FormatOptions where 2 | 3 | import Prelude 4 | 5 | import ArgParse.Basic (ArgParser) 6 | import ArgParse.Basic as Arg 7 | import Control.Alt ((<|>)) 8 | import Control.Monad.Error.Class (throwError) 9 | import Data.Argonaut.Core (Json, jsonEmptyObject, jsonNull) 10 | import Data.Argonaut.Core as Json 11 | import Data.Argonaut.Decode (JsonDecodeError(..), decodeJson, (.:?)) 12 | import Data.Argonaut.Encode (assoc, encodeJson, extend) 13 | import Data.Either (Either) 14 | import Data.Maybe (Maybe(..), fromMaybe, maybe) 15 | import Data.Traversable (traverse) 16 | import Tidy (ImportSortOption(..), ImportWrapOption(..), TypeArrowOption(..), UnicodeOption(..)) 17 | 18 | type FormatOptions = 19 | { importSort :: ImportSortOption 20 | , importWrap :: ImportWrapOption 21 | , indent :: Int 22 | , operatorsFile :: Maybe String 23 | , ribbon :: Number 24 | , typeArrowPlacement :: TypeArrowOption 25 | , unicode :: UnicodeOption 26 | , width :: Maybe Int 27 | } 28 | 29 | defaults :: FormatOptions 30 | defaults = 31 | { importSort: ImportSortSource 32 | , importWrap: ImportWrapSource 33 | , indent: 2 34 | , operatorsFile: Nothing 35 | , ribbon: 1.0 36 | , typeArrowPlacement: TypeArrowFirst 37 | , unicode: UnicodeSource 38 | , width: Nothing 39 | } 40 | 41 | formatOptions :: ArgParser FormatOptions 42 | formatOptions = 43 | Arg.fromRecord 44 | { importSort: 45 | Arg.choose "import sort" 46 | [ Arg.flag [ "--import-sort-source", "-iss" ] 47 | "Imports are not automatically sorted and keep the same order as in the source.\nDefault." 48 | $> ImportSortSource 49 | , Arg.flag [ "--import-sort-ide", "-isi" ] 50 | "Imports are automatically sorted like purs-ide." 51 | $> ImportSortIde 52 | ] 53 | # Arg.default defaults.importSort 54 | , importWrap: 55 | Arg.choose "import wrap" 56 | [ Arg.flag [ "--import-wrap-source", "-iws" ] 57 | "Imports are wrapped only when breaks are in the source.\nDefault. Works well with IDE imports." 58 | $> ImportWrapSource 59 | , Arg.flag [ "--import-wrap-auto", "-iwa" ] 60 | "Imports are wrapped based on the `--width` option." 61 | $> ImportWrapAuto 62 | ] 63 | # Arg.default defaults.importWrap 64 | , indent: 65 | Arg.argument [ "--indent", "-i" ] 66 | "Number of spaces to use as indentation.\nDefaults to 2." 67 | # Arg.int 68 | # Arg.default defaults.indent 69 | , operatorsFile: 70 | Arg.argument [ "--operators", "-o" ] 71 | "Path to an operator table generated by `generate-operators`.\nDefault is to use a pre-generated table of core and contrib." 72 | # Arg.unformat "FILE_PATH" pure 73 | # Arg.optional 74 | , ribbon: 75 | Arg.argument [ "--ribbon", "-r" ] 76 | "The ratio of printable width to maximum width.\nFrom 0 to 1. Defaults to 1." 77 | # Arg.number 78 | # Arg.default defaults.ribbon 79 | , typeArrowPlacement: 80 | Arg.choose "type arrow placement" 81 | [ Arg.flag [ "--arrow-first", "-af" ] 82 | "Type signatures put arrows first on the line.\nDefault." 83 | $> TypeArrowFirst 84 | , Arg.flag [ "--arrow-last", "-al" ] 85 | "Type signatures put arrows last on the line." 86 | $> TypeArrowLast 87 | ] 88 | # Arg.default defaults.typeArrowPlacement 89 | , unicode: unicodeOption 90 | , width: 91 | Arg.argument [ "--width", "-w" ] 92 | "The maximum width of the document in columns.\nDefaults to no maximum." 93 | # Arg.int 94 | # Arg.optional 95 | } 96 | 97 | unicodeOption :: ArgParser UnicodeOption 98 | unicodeOption = 99 | Arg.choose "unicode argument" 100 | [ Arg.flag [ "--unicode-source", "-us" ] 101 | "Unicode punctuation is rendered as it appears in the source input.\nDefault." 102 | $> UnicodeSource 103 | , Arg.flag [ "--unicode-always", "-ua" ] 104 | "Unicode punctuation is always preferred." 105 | $> UnicodeAlways 106 | , Arg.flag [ "--unicode-never", "-un" ] 107 | "Unicode punctuation is never preferred." 108 | $> UnicodeNever 109 | ] 110 | # Arg.default defaults.unicode 111 | 112 | fromJson :: Json -> Either JsonDecodeError FormatOptions 113 | fromJson json = do 114 | obj <- decodeJson json 115 | importSort <- traverse importSortFromString =<< obj .:? "importSort" 116 | importWrap <- traverse importWrapFromString =<< obj .:? "importWrap" 117 | indent <- obj .:? "indent" 118 | operatorsFile <- obj .:? "operatorsFile" 119 | ribbon <- obj .:? "ribbon" 120 | typeArrowPlacement <- traverse typeArrowPlacementFromString =<< obj .:? "typeArrowPlacement" 121 | unicode <- traverse unicodeFromString =<< obj .:? "unicode" 122 | width <- obj .:? "width" 123 | pure 124 | { importSort: fromMaybe defaults.importSort importSort 125 | , importWrap: fromMaybe defaults.importWrap importWrap 126 | , indent: fromMaybe defaults.indent indent 127 | , operatorsFile: operatorsFile <|> defaults.operatorsFile 128 | , ribbon: fromMaybe defaults.ribbon ribbon 129 | , typeArrowPlacement: fromMaybe defaults.typeArrowPlacement typeArrowPlacement 130 | , unicode: fromMaybe defaults.unicode unicode 131 | , width: width <|> defaults.width 132 | } 133 | 134 | toJson :: FormatOptions -> Json 135 | toJson options = 136 | jsonEmptyObject 137 | # extend (assoc "importSort" (importSortToString options.importSort)) 138 | # extend (assoc "importWrap" (importWrapToString options.importWrap)) 139 | # extend (assoc "indent" options.indent) 140 | # extend (assoc "operatorsFile" (maybe jsonNull encodeJson options.operatorsFile)) 141 | # extend (assoc "ribbon" options.ribbon) 142 | # extend (assoc "typeArrowPlacement" (typeArrowPlacementToString options.typeArrowPlacement)) 143 | # extend (assoc "unicode" (unicodeToString options.unicode)) 144 | # extend (assoc "width" (maybe jsonNull encodeJson options.width)) 145 | 146 | typeArrowPlacementFromString :: String -> Either JsonDecodeError TypeArrowOption 147 | typeArrowPlacementFromString = case _ of 148 | "first" -> pure TypeArrowFirst 149 | "last" -> pure TypeArrowLast 150 | other -> throwError $ UnexpectedValue (Json.fromString other) 151 | 152 | typeArrowPlacementToString :: TypeArrowOption -> String 153 | typeArrowPlacementToString = case _ of 154 | TypeArrowFirst -> "first" 155 | TypeArrowLast -> "last" 156 | 157 | unicodeFromString :: String -> Either JsonDecodeError UnicodeOption 158 | unicodeFromString = case _ of 159 | "source" -> pure UnicodeSource 160 | "always" -> pure UnicodeAlways 161 | "never" -> pure UnicodeNever 162 | other -> throwError $ UnexpectedValue (Json.fromString other) 163 | 164 | unicodeToString :: UnicodeOption -> String 165 | unicodeToString = case _ of 166 | UnicodeSource -> "source" 167 | UnicodeAlways -> "always" 168 | UnicodeNever -> "never" 169 | 170 | importWrapFromString :: String -> Either JsonDecodeError ImportWrapOption 171 | importWrapFromString = case _ of 172 | "source" -> pure ImportWrapSource 173 | "auto" -> pure ImportWrapAuto 174 | other -> throwError $ UnexpectedValue (Json.fromString other) 175 | 176 | importWrapToString :: ImportWrapOption -> String 177 | importWrapToString = case _ of 178 | ImportWrapSource -> "source" 179 | ImportWrapAuto -> "auto" 180 | 181 | importSortFromString :: String -> Either JsonDecodeError ImportSortOption 182 | importSortFromString = case _ of 183 | "source" -> pure ImportSortSource 184 | "ide" -> pure ImportSortIde 185 | other -> throwError $ UnexpectedValue (Json.fromString other) 186 | 187 | importSortToString :: ImportSortOption -> String 188 | importSortToString = case _ of 189 | ImportSortSource -> "source" 190 | ImportSortIde -> "ide" 191 | -------------------------------------------------------------------------------- /bin/src/Bin/Timing.js: -------------------------------------------------------------------------------- 1 | import process from "process"; 2 | 3 | export function hrtime() { 4 | var t = process.hrtime() 5 | return { seconds: t[0], nanos: t[1] }; 6 | } 7 | 8 | export function hrtimeDiff(old) { 9 | return function() { 10 | var t = process.hrtime([old.seconds, old.nanos]); 11 | return { seconds: t[0], nanos: t[1] }; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /bin/src/Bin/Timing.purs: -------------------------------------------------------------------------------- 1 | module Bin.Timing where 2 | 3 | import Prelude 4 | 5 | import Data.Time.Duration (Milliseconds(..)) 6 | import Effect (Effect) 7 | 8 | type Timing = 9 | { seconds :: Number 10 | , nanos :: Number 11 | } 12 | 13 | foreign import hrtime :: Effect Timing 14 | 15 | foreign import hrtimeDiff :: Timing -> Effect Timing 16 | 17 | toMilliseconds :: Timing -> Milliseconds 18 | toMilliseconds { seconds, nanos } = 19 | Milliseconds $ seconds * 1000.0 + nanos / 1000000.0 20 | -------------------------------------------------------------------------------- /bin/src/Bin/Version.js: -------------------------------------------------------------------------------- 1 | export const version = "v0.11.0"; 2 | -------------------------------------------------------------------------------- /bin/src/Bin/Version.purs: -------------------------------------------------------------------------------- 1 | module Bin.Version where 2 | 3 | foreign import version :: String 4 | -------------------------------------------------------------------------------- /bin/src/Bin/Worker.purs: -------------------------------------------------------------------------------- 1 | module Bin.Worker where 2 | 3 | import Prelude 4 | 5 | import Bin.FormatOptions (FormatOptions) 6 | import Bin.FormatOptions as FormatOptions 7 | import Bin.Timing (hrtime, hrtimeDiff, toMilliseconds) 8 | import Data.Array.NonEmpty as NonEmptyArray 9 | import Data.Either (Either(..), either, fromRight') 10 | import Data.Lazy (Lazy) 11 | import Data.Lazy as Lazy 12 | import Data.Map as Map 13 | import Data.Maybe (Maybe(..), fromMaybe, maybe) 14 | import Data.Monoid (power) 15 | import Data.Newtype (unwrap) 16 | import Dodo as Dodo 17 | import Effect (Effect) 18 | import Effect.Aff (Aff, runAff_, throwError) 19 | import Effect.Class (liftEffect) 20 | import Foreign.Object (Object) 21 | import Foreign.Object as Object 22 | import Node.Encoding (Encoding(..)) 23 | import Node.FS.Aff as FS 24 | import Node.Path (FilePath) 25 | import Node.WorkerBees as Worker 26 | import Partial.Unsafe (unsafeCrashWith) 27 | import PureScript.CST (RecoveredParserResult(..), parseModule) 28 | import PureScript.CST.Errors (printParseError) 29 | import PureScript.CST.Parser.Monad (PositionedError) 30 | import Tidy (defaultFormatOptions, formatModule, toDoc) 31 | import Tidy.Operators (parseOperatorTable) 32 | import Tidy.Precedence (PrecedenceMap, remapOperators) 33 | 34 | type WorkerConfig = 35 | { importSort :: String 36 | , importWrap :: String 37 | , indent :: Int 38 | , operatorsFile :: String 39 | , ribbon :: Number 40 | , typeArrowPlacement :: String 41 | , unicode :: String 42 | , width :: Int 43 | } 44 | 45 | toWorkerConfig :: FormatOptions -> WorkerConfig 46 | toWorkerConfig options = 47 | { importSort: FormatOptions.importSortToString options.importSort 48 | , importWrap: FormatOptions.importWrapToString options.importWrap 49 | , indent: options.indent 50 | , operatorsFile: fromMaybe ".tidyoperators.default" options.operatorsFile 51 | , ribbon: options.ribbon 52 | , typeArrowPlacement: FormatOptions.typeArrowPlacementToString options.typeArrowPlacement 53 | , unicode: FormatOptions.unicodeToString options.unicode 54 | , width: fromMaybe top options.width 55 | } 56 | 57 | type WorkerData = 58 | { shouldCheck :: Boolean 59 | , operatorsByPath :: Object (Array String) 60 | } 61 | 62 | type WorkerInput = 63 | { filePath :: FilePath 64 | , config :: WorkerConfig 65 | } 66 | 67 | type WorkerOutput = 68 | { filePath :: FilePath 69 | , error :: String 70 | , alreadyFormatted :: Boolean 71 | , timing :: Number 72 | } 73 | 74 | formatCommand :: FormatOptions -> PrecedenceMap -> String -> Either String String 75 | formatCommand args operators contents = do 76 | let 77 | print = Dodo.print Dodo.plainText 78 | { pageWidth: fromMaybe top args.width 79 | , ribbonRatio: args.ribbon 80 | , indentWidth: args.indent 81 | , indentUnit: power " " args.indent 82 | } 83 | 84 | case parseModule contents of 85 | ParseSucceeded ok -> do 86 | let 87 | opts = defaultFormatOptions 88 | { importSort = args.importSort 89 | , importWrap = args.importWrap 90 | , operators = remapOperators operators ok 91 | , typeArrowPlacement = args.typeArrowPlacement 92 | , unicode = args.unicode 93 | } 94 | Right $ print $ toDoc $ formatModule opts ok 95 | ParseSucceededWithErrors _ errs -> do 96 | Left $ printPositionedError $ NonEmptyArray.head errs 97 | ParseFailed err -> 98 | Left $ printPositionedError err 99 | 100 | printPositionedError :: PositionedError -> String 101 | printPositionedError { error, position } = 102 | "[" <> show (position.line + 1) <> ":" <> show (position.column + 1) <> "] " <> printParseError error 103 | 104 | formatInPlaceCommand :: Boolean -> PrecedenceMap -> WorkerInput -> Aff WorkerOutput 105 | formatInPlaceCommand shouldCheck operators { filePath, config } = do 106 | let 107 | formatOptions :: FormatOptions 108 | formatOptions = 109 | { importSort: 110 | fromRight' (\_ -> unsafeCrashWith "Unknown importSort value") do 111 | FormatOptions.importSortFromString config.importSort 112 | , importWrap: 113 | fromRight' (\_ -> unsafeCrashWith "Unknown importWrap value") do 114 | FormatOptions.importWrapFromString config.importWrap 115 | , indent: config.indent 116 | , operatorsFile: Nothing 117 | , ribbon: config.ribbon 118 | , typeArrowPlacement: 119 | fromRight' (\_ -> unsafeCrashWith "Unknown typeArrowPlacement value") do 120 | FormatOptions.typeArrowPlacementFromString config.typeArrowPlacement 121 | , unicode: 122 | fromRight' (\_ -> unsafeCrashWith "Unknown unicode value") do 123 | FormatOptions.unicodeFromString config.unicode 124 | , width: Just config.width 125 | } 126 | contents <- FS.readTextFile UTF8 filePath 127 | start <- liftEffect hrtime 128 | case formatCommand formatOptions operators contents of 129 | Right formatted -> do 130 | timing <- map (unwrap <<< toMilliseconds) $ liftEffect $ hrtimeDiff start 131 | if shouldCheck then do 132 | let alreadyFormatted = formatted == contents 133 | pure { filePath, error: "", alreadyFormatted, timing } 134 | else do 135 | FS.writeTextFile UTF8 filePath formatted 136 | pure { filePath, error: "", alreadyFormatted: false, timing } 137 | Left error -> 138 | pure { filePath, error, alreadyFormatted: false, timing: zero } 139 | 140 | main :: Effect Unit 141 | main = Worker.makeAsMain \{ receive, reply, workerData: { shouldCheck, operatorsByPath } } -> do 142 | let 143 | parsedOperatorsByPath :: Object (Lazy PrecedenceMap) 144 | parsedOperatorsByPath = 145 | (\operators -> Lazy.defer \_ -> parseOperatorTable operators) <$> operatorsByPath 146 | 147 | receive \input@{ config } -> do 148 | let 149 | operators :: PrecedenceMap 150 | operators = 151 | maybe Map.empty Lazy.force $ Object.lookup config.operatorsFile parsedOperatorsByPath 152 | runAff_ 153 | (either throwError reply) 154 | (formatInPlaceCommand shouldCheck operators input) 155 | -------------------------------------------------------------------------------- /bin/src/Main.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | 5 | import ArgParse.Basic (ArgParser) 6 | import ArgParse.Basic as Arg 7 | import Bin.FormatOptions (FormatOptions, formatOptions) 8 | import Bin.FormatOptions as FormatOptions 9 | import Bin.Version (version) 10 | import Bin.Worker (WorkerData, WorkerInput, WorkerOutput, formatCommand, formatInPlaceCommand, toWorkerConfig) 11 | import Control.Monad.State (evalStateT, lift) 12 | import Control.Monad.State as State 13 | import Control.Parallel (parTraverse) 14 | import Control.Plus ((<|>)) 15 | import Data.Argonaut.Core as Json 16 | import Data.Argonaut.Decode (parseJson, printJsonDecodeError) 17 | import Data.Array as Array 18 | import Data.Either (Either(..), isLeft) 19 | import Data.Foldable (fold, foldMap, foldl, for_, oneOf) 20 | import Data.Lazy (Lazy) 21 | import Data.Lazy as Lazy 22 | import Data.List (List) 23 | import Data.List as List 24 | import Data.Map (Map) 25 | import Data.Map as Map 26 | import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe) 27 | import Data.Monoid (guard) 28 | import Data.Newtype (unwrap) 29 | import Data.Number.Format as NF 30 | import Data.Set as Set 31 | import Data.String (Pattern(..)) 32 | import Data.String as String 33 | import Data.Traversable (for, traverse) 34 | import Data.Tuple (Tuple(..)) 35 | import Data.Tuple.Nested ((/\)) 36 | import Effect (Effect) 37 | import Effect.Aff (Aff, effectCanceler, error, launchAff_, makeAff, throwError, try) 38 | import Effect.Aff as Aff 39 | import Effect.Class (liftEffect) 40 | import Effect.Class.Console as Console 41 | import Effect.Ref as Ref 42 | import Foreign.Object (Object) 43 | import Foreign.Object as Object 44 | import Node.Buffer as Buffer 45 | import Node.Encoding (Encoding(..)) 46 | import Node.EventEmitter (on) 47 | import Node.FS.Aff as FS 48 | import Node.FS.Stats as Stats 49 | import Node.Glob.Basic (expandGlobsCwd, expandGlobsWithStatsCwd) 50 | import Node.Path (FilePath) 51 | import Node.Path as Path 52 | import Node.Process as Process 53 | import Node.Stream as Stream 54 | import Node.WorkerBees as Worker 55 | import Node.WorkerBees.Aff.Pool (poolTraverse) 56 | import PureScript.CST (RecoveredParserResult(..), parseModule, toRecovered) 57 | import PureScript.CST.ModuleGraph (ModuleSort(..), sortModules) 58 | import PureScript.CST.Types (Module(..), ModuleHeader(..), Name(..)) 59 | import Tidy.Operators (parseOperatorTable, resolveOperatorExports) 60 | import Tidy.Operators.Defaults (defaultOperators) 61 | import Tidy.Precedence (OperatorNamespace(..), PrecedenceMap) 62 | 63 | data FormatMode = Check | Write 64 | 65 | derive instance Eq FormatMode 66 | 67 | data ConfigOption 68 | = Require 69 | | Ignore 70 | | Prefer 71 | 72 | data Command 73 | = GenerateOperators (Array String) 74 | | GenerateRc FormatOptions 75 | | FormatInPlace FormatMode FormatOptions ConfigOption Int Boolean (Array String) 76 | | Format FormatOptions ConfigOption 77 | 78 | rcFileName :: String 79 | rcFileName = ".tidyrc.json" 80 | 81 | parser :: ArgParser Command 82 | parser = 83 | Arg.choose "command" 84 | [ Arg.command [ "generate-operators" ] 85 | "Generate an operator precedence table for better operator formatting.\nBest used with `spago sources`. Prints to stdout." 86 | do 87 | GenerateOperators <$> pursGlobs 88 | <* Arg.flagHelp 89 | , Arg.command [ "generate-config" ] 90 | "Writes a .tidyrc file to the current working directory based\non the command line options given." 91 | do 92 | GenerateRc <$> formatOptions 93 | <* Arg.flagHelp 94 | , Arg.command [ "format-in-place" ] 95 | "Format source files in place." 96 | do 97 | FormatInPlace Write 98 | <$> formatOptions 99 | <*> configOption 100 | <*> workerOptions 101 | <*> timingOption 102 | <*> pursGlobs 103 | <* Arg.flagHelp 104 | , Arg.command [ "format" ] 105 | "Format input over stdin." 106 | do 107 | Format 108 | <$> formatOptions 109 | <*> configOption 110 | <* Arg.flagHelp 111 | , Arg.command [ "check" ] 112 | "Check source files are formatted." 113 | do 114 | FormatInPlace Check 115 | <$> formatOptions 116 | <*> configOption 117 | <*> workerOptions 118 | <*> timingOption 119 | <*> pursGlobs 120 | <* Arg.flagHelp 121 | ] 122 | <* Arg.flagInfo [ "--version", "-v" ] "Shows the current version." version 123 | <* Arg.flagHelp 124 | where 125 | pursGlobs = 126 | Arg.anyNotFlag "PURS_GLOB" "Globs for PureScript sources." 127 | # Arg.unfolded1 128 | 129 | workerOptions = 130 | Arg.argument [ "--threads", "-t" ] 131 | "Number of worker threads to use.\nDefaults to 4." 132 | # Arg.int 133 | # Arg.default 4 134 | 135 | configOption = 136 | Arg.choose "config behavior" 137 | [ Arg.flag [ "--config-prefer", "-cp" ] 138 | "Always use config files when present, otherwise use CLI options.\nDefault." 139 | $> Prefer 140 | , Arg.flag [ "--config-require", "-cr" ] 141 | "Require the presence of a config file.\nUseful for editors." 142 | $> Require 143 | , Arg.flag [ "--config-ignore", "-ci" ] 144 | "Ignore all configuration files and only use CLI options.\nNot recommended." 145 | $> Ignore 146 | ] 147 | # Arg.default Prefer 148 | 149 | timingOption = 150 | Arg.flag [ "--timing" ] 151 | "Print the time spent formatting each file." 152 | # Arg.boolean 153 | # Arg.default false 154 | 155 | main :: Effect Unit 156 | main = launchAff_ do 157 | args <- Array.drop 2 <$> liftEffect Process.argv 158 | let 159 | parsedCmd = 160 | Arg.parseArgs "purs-tidy" "A tidy-upper for PureScript source code." parser args 161 | 162 | case parsedCmd of 163 | Left err -> do 164 | Console.error $ Arg.printArgError err 165 | case err of 166 | Arg.ArgError _ Arg.ShowHelp -> 167 | pure unit 168 | Arg.ArgError _ (Arg.ShowInfo _) -> 169 | pure unit 170 | _ -> 171 | liftEffect $ Process.setExitCode 1 172 | Right cmd -> 173 | case cmd of 174 | GenerateOperators globs -> 175 | generateOperatorsCommand globs 176 | 177 | GenerateRc cliOptions -> do 178 | rcStats <- Aff.try $ FS.stat rcFileName 179 | if isLeft rcStats then do 180 | let contents = Json.stringifyWithIndent 2 $ FormatOptions.toJson cliOptions 181 | FS.writeTextFile UTF8 rcFileName $ contents <> "\n" 182 | else do 183 | Console.error $ rcFileName <> " already exists." 184 | liftEffect $ Process.setExitCode 1 185 | 186 | FormatInPlace mode cliOptions configOption numThreads printTiming globs -> do 187 | currentDir <- liftEffect Process.cwd 188 | let root = (Path.parse currentDir).root 189 | srcLocation <- fold <$> liftEffect (Process.lookupEnv "TIDY_INSTALL_LOC") 190 | files <- expandGlobs globs 191 | filesWithOptions <- flip evalStateT Map.empty do 192 | for files \filePath -> do 193 | rcMap <- State.get 194 | rcOptions <- State.state <<< const =<< lift (resolveRcForDir root rcMap (Path.dirname filePath)) 195 | options <- lift $ getOptions cliOptions rcOptions filePath configOption 196 | pure 197 | { filePath 198 | , config: toWorkerConfig options 199 | } 200 | 201 | operatorsByPath <- 202 | filesWithOptions 203 | # map _.config.operatorsFile 204 | # Array.nub 205 | # parTraverse (\path -> Tuple path <$> readOperatorTable path) 206 | # map Object.fromFoldable 207 | 208 | let 209 | workerData = 210 | { shouldCheck: mode == Check 211 | , operatorsByPath 212 | } 213 | 214 | results <- 215 | if Array.length filesWithOptions > numThreads * 2 then do 216 | -- Worker location for production bin 217 | let bundleLocation = Path.concat [ srcLocation, "bundle", "Bin.Worker", "index.js" ] 218 | -- Worker location for local dev 219 | let outputLocation = Path.concat [ srcLocation, "output", "Bin.Worker", "index.js" ] 220 | worker <- 221 | oneOf 222 | [ FS.stat bundleLocation $> Worker.unsafeWorkerFromPath bundleLocation 223 | , FS.stat outputLocation $> Worker.unsafeWorkerFromPath outputLocation 224 | ] 225 | <|> throwError (error "Worker not found") 226 | poolTraverse worker workerData numThreads filesWithOptions 227 | else 228 | parTraverse (formatInPlaceOne workerData) filesWithOptions 229 | 230 | let 231 | { errors, notFormatted } = 232 | results # foldMap \{ filePath, error, alreadyFormatted } -> 233 | { errors: guard (not String.null error) [ filePath /\ error ] 234 | , notFormatted: guard (not alreadyFormatted) [ filePath ] 235 | } 236 | 237 | when printTiming do 238 | for_ (Array.sortBy (comparing _.timing) results) \{ filePath, timing } -> 239 | when (timing > 0.0) do 240 | Console.error $ fold 241 | [ Path.relative currentDir filePath 242 | , " " 243 | , NF.toStringWith (NF.fixed 2) timing 244 | , "ms" 245 | ] 246 | 247 | case mode of 248 | Write -> 249 | for_ errors \(Tuple filePath error) -> 250 | Console.error $ filePath <> ":\n " <> error <> "\n" 251 | 252 | Check -> liftEffect do 253 | if Array.null errors && Array.null notFormatted then do 254 | Console.log "All files are formatted." 255 | Process.setExitCode 0 256 | else do 257 | unless (Array.null errors) do 258 | Console.error "Some files have errors:\n" 259 | for_ errors \(Tuple filePath error) -> 260 | Console.error $ filePath <> ":\n " <> error <> "\n" 261 | unless (Array.null notFormatted) do 262 | Console.error "Some files are not formatted:\n" 263 | for_ notFormatted Console.error 264 | Process.setExitCode 1 265 | 266 | Format cliOptions configOption -> do 267 | currentDir <- liftEffect Process.cwd 268 | let root = (Path.parse currentDir).root 269 | Tuple rcOptions _ <- resolveRcForDir root Map.empty currentDir 270 | options <- getOptions cliOptions rcOptions "the current directory." configOption 271 | operators <- parseOperatorTable <<< fromMaybe defaultOperators <$> traverse readOperatorTable options.operatorsFile 272 | contents <- readStdin 273 | case formatCommand options operators contents of 274 | Left err -> do 275 | Console.error err 276 | liftEffect $ Process.setExitCode 1 277 | Right str -> 278 | writeStdout str 279 | 280 | expandGlobs :: Array String -> Aff (Array String) 281 | expandGlobs = map dirToGlob >>> expandGlobsWithStatsCwd >>> map onlyFiles 282 | where 283 | dirToGlob path = 284 | if Path.extname path == "" then 285 | if isJust (String.stripSuffix (Pattern "**") path) then 286 | Path.concat [ path, "*.purs" ] 287 | else 288 | Path.concat [ path, "**", "*.purs" ] 289 | else 290 | path 291 | 292 | onlyFiles = 293 | Map.filter Stats.isFile 294 | >>> Map.keys 295 | >>> Set.toUnfoldable 296 | 297 | getOptions :: FormatOptions -> Maybe FormatOptions -> FilePath -> ConfigOption -> Aff FormatOptions 298 | getOptions cliOptions rcOptions filePath = case _ of 299 | Prefer -> pure $ fromMaybe cliOptions rcOptions 300 | Ignore -> pure cliOptions 301 | Require -> 302 | case rcOptions of 303 | Nothing -> do 304 | Console.error $ rcFileName <> " not found for " <> filePath 305 | liftEffect $ Process.exit' 1 306 | Just options -> 307 | pure options 308 | 309 | readOperatorTable :: FilePath -> Aff (Array String) 310 | readOperatorTable path 311 | | path == ".tidyoperators.default" = pure defaultOperators 312 | | otherwise = String.split (Pattern "\n") <$> FS.readTextFile UTF8 path 313 | 314 | formatInPlaceOne :: WorkerData -> WorkerInput -> Aff WorkerOutput 315 | formatInPlaceOne { shouldCheck, operatorsByPath } input@{ config } = do 316 | let 317 | parsedOperatorsByPath :: Object (Lazy PrecedenceMap) 318 | parsedOperatorsByPath = 319 | (\operators -> Lazy.defer \_ -> parseOperatorTable operators) <$> operatorsByPath 320 | 321 | let 322 | operators :: PrecedenceMap 323 | operators = 324 | maybe Map.empty Lazy.force $ Object.lookup config.operatorsFile parsedOperatorsByPath 325 | 326 | formatInPlaceCommand shouldCheck operators input 327 | 328 | type RcMap = Map FilePath (Maybe FormatOptions) 329 | 330 | resolveRcForDir :: FilePath -> RcMap -> FilePath -> Aff (Tuple (Maybe FormatOptions) RcMap) 331 | resolveRcForDir root = go List.Nil 332 | where 333 | go :: List FilePath -> RcMap -> FilePath -> Aff (Tuple (Maybe FormatOptions) RcMap) 334 | go paths cache dir = case Map.lookup dir cache of 335 | Just res -> 336 | pure $ unwind cache res paths 337 | Nothing -> do 338 | let filePath = Path.concat [ dir, rcFileName ] 339 | contents <- try $ FS.readTextFile UTF8 filePath 340 | case contents of 341 | Left _ 342 | | dir == root -> 343 | pure $ unwind cache Nothing (List.Cons dir paths) 344 | | otherwise -> 345 | go (List.Cons dir paths) cache (Path.dirname dir) 346 | Right contents' -> 347 | case FormatOptions.fromJson =<< parseJson contents' of 348 | Left jsonError -> 349 | throwError $ error $ "Could not decode " <> filePath <> ": " <> printJsonDecodeError jsonError 350 | Right options -> do 351 | operatorsFile <- liftEffect $ traverse (Path.resolve [ dir ]) options.operatorsFile 352 | pure $ unwind cache (Just options { operatorsFile = operatorsFile }) (List.Cons dir paths) 353 | 354 | unwind :: RcMap -> Maybe FormatOptions -> List FilePath -> Tuple (Maybe FormatOptions) RcMap 355 | unwind cache res = case _ of 356 | List.Cons p ps -> 357 | unwind (Map.insert p res cache) res ps 358 | List.Nil -> 359 | Tuple res cache 360 | 361 | readStdin :: Aff String 362 | readStdin = makeAff \k -> do 363 | contents <- Ref.new [] 364 | c1 <- Process.stdin # on Stream.dataH \buff -> 365 | void $ Ref.modify (_ `Array.snoc` buff) contents 366 | c2 <- Process.stdin # on Stream.endH do 367 | k <<< Right =<< Buffer.toString UTF8 =<< Buffer.concat =<< Ref.read contents 368 | pure $ effectCanceler (c1 *> c2) 369 | 370 | writeStdout :: String -> Aff Unit 371 | writeStdout str = makeAff \k -> do 372 | _ <- Stream.writeString' Process.stdout UTF8 str (const (k (Right unit))) 373 | pure mempty 374 | 375 | generateOperatorsCommand :: Array String -> Aff Unit 376 | generateOperatorsCommand globs = do 377 | sourcePaths <- expandGlobsCwd globs 378 | modules <- 379 | sourcePaths # Array.fromFoldable # parTraverse \path -> do 380 | contents <- liftEffect <<< Buffer.toString UTF8 =<< FS.readFile path 381 | pure $ parseModule contents 382 | 383 | let 384 | parsedModules = modules # Array.mapMaybe case _ of 385 | ParseSucceeded m -> Just $ toRecovered m 386 | ParseSucceededWithErrors m _ -> Just m 387 | _ -> Nothing 388 | 389 | case sortModules (_.header <<< unwrap) parsedModules of 390 | CycleDetected ms -> do 391 | let getModuleName (Module { header: ModuleHeader { name: Name { name } } }) = name 392 | let modNames = map (unwrap <<< getModuleName) ms 393 | Console.error $ String.joinWith "\n" 394 | [ "Cycle detected in modules:" 395 | , String.joinWith "\n" $ map (append " ") modNames 396 | ] 397 | Sorted sorted -> do 398 | let precMap = foldl resolveOperatorExports Map.empty sorted 399 | for_ (Map.toUnfoldable precMap :: Array _) \(Tuple mbModName ops) -> do 400 | let modName = foldMap unwrap mbModName 401 | for_ (Map.toUnfoldable ops :: Array _) \(Tuple (Tuple ns op) prec) -> do 402 | Console.log $ fold 403 | [ modName 404 | , case ns of 405 | OperatorType -> ".(" <> unwrap op <> ")" <> " type" 406 | OperatorValue -> ".(" <> unwrap op <> ")" 407 | , " " <> show prec 408 | ] 409 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "purs-tidy", 3 | "version": "0.11.0", 4 | "type": "module", 5 | "description": "A syntax tidy-upper (formatter) for PureScript.", 6 | "bin": { 7 | "purs-tidy": "bin/index.js" 8 | }, 9 | "directories": { 10 | "test": "test" 11 | }, 12 | "files": [ 13 | "bin/index.js", 14 | "bundle/**/*.js" 15 | ], 16 | "scripts": { 17 | "version": "echo 'export const version = \"v'$npm_package_version'\";' > ./bin/Bin/Version.js && git add ./bin/Bin/Version.js", 18 | "postversion": "git push && git push --tags", 19 | "build": "spago build --quiet", 20 | "bundle": "npm run build && purs-backend-es build --int-tags && npm run bundle:main && npm run bundle:worker", 21 | "bundle:main": "purs-backend-es bundle-module --platform node --to \"./bundle/Main/index.js\" --no-build --minify", 22 | "bundle:worker": "purs-backend-es bundle-app --platform node --to \"./bundle/Bin.Worker/index.js\" --main Bin.Worker --no-build --minify", 23 | "test": "spago test", 24 | "generate-default-operators": "spago run -p tidy-script -m GenerateDefaultOperatorsModule", 25 | "format-self": "npm run build && node ./bin/index.js format-in-place src bin script test-snapshots", 26 | "check-self": "node ./bin/index.js check src 'test/*.purs' src bin script test-snapshots", 27 | "prepublishOnly": "rm -rf output bundle && npm run bundle" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/natefaubion/purescript-tidy.git" 32 | }, 33 | "keywords": [ 34 | "PureScript", 35 | "formatter", 36 | "format" 37 | ], 38 | "author": "Nathan Faubion (https://github.com/natefaubion/)", 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/natefaubion/purescript-tidy/issues" 42 | }, 43 | "homepage": "https://github.com/natefaubion/purescript-tidy#readme", 44 | "devDependencies": { 45 | "esbuild": "^0.25.0", 46 | "purescript": "^0.15.15", 47 | "purs-backend-es": "^1.4.2", 48 | "purs-tidy": "^0.11.0", 49 | "spago": "^0.93.43" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /script/spago.yaml: -------------------------------------------------------------------------------- 1 | package: 2 | name: tidy-script 3 | build: 4 | strict: true 5 | dependencies: 6 | - arrays: ">=7.3.0 <8.0.0" 7 | - console: ">=6.1.0 <7.0.0" 8 | - effect: ">=4.0.0 <5.0.0" 9 | - exceptions: ">=6.1.0 <7.0.0" 10 | - maybe: ">=6.0.0 <7.0.0" 11 | - node-buffer: ">=9.0.0 <10.0.0" 12 | - node-child-process: ">=11.1.0 <12.0.0" 13 | - node-fs: ">=9.2.0 <10.0.0" 14 | - node-path: ">=5.0.1 <6.0.0" 15 | - node-process: ">=11.2.0 <12.0.0" 16 | - prelude: ">=6.0.1 <7.0.0" 17 | - strings: ">=6.0.1 <7.0.0" 18 | - unsafe-coerce: ">=6.0.0 <7.0.0" 19 | -------------------------------------------------------------------------------- /script/src/GenerateDefaultOperatorsModule.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import os from "os"; 3 | import path from "path"; 4 | 5 | export function tmpdir(prefix) { 6 | return () => 7 | fs.mkdtempSync(path.join(os.tmpdir(), prefix), "utf-8"); 8 | } 9 | -------------------------------------------------------------------------------- /script/src/GenerateDefaultOperatorsModule.purs: -------------------------------------------------------------------------------- 1 | module GenerateDefaultOperatorsModule where 2 | 3 | import Prelude 4 | 5 | import Data.Array (mapWithIndex) 6 | import Data.Array as Array 7 | import Data.Maybe (Maybe(..)) 8 | import Data.String (Pattern(..)) 9 | import Data.String as String 10 | import Effect (Effect) 11 | import Effect.Class.Console as Console 12 | import Effect.Exception (catchException, throwException) 13 | import Node.Buffer (Buffer) 14 | import Node.Buffer as Buffer 15 | import Node.ChildProcess as ChildProcess 16 | import Node.Encoding (Encoding(..)) 17 | import Node.FS.Sync (writeTextFile) 18 | import Node.Path as Path 19 | import Node.Process (cwd) 20 | import Unsafe.Coerce (unsafeCoerce) 21 | 22 | foreign import tmpdir :: String -> Effect String 23 | 24 | main :: Effect Unit 25 | main = do 26 | cwdPath <- cwd 27 | tmpPath <- tmpdir "purs-tidy-generate-default-operators-" 28 | 29 | let opts = _ { cwd = Just tmpPath } 30 | let genCmd = Path.concat [ cwdPath, "bin", "index.js" ] <> " generate-operators '.spago/*/*/src/**/*.purs'" 31 | 32 | writeTextFile UTF8 (Path.concat [ tmpPath, "spago.yaml" ]) defaultSpagoYaml 33 | writeTextFile UTF8 (Path.concat [ tmpPath, "package.json" ]) defaultPackageJson 34 | _ <- ChildProcess.execSync' "npm install" opts 35 | output <- Buffer.toString UTF8 =<< catchException 36 | ( \err -> do 37 | stdout <- Buffer.toString UTF8 ((unsafeCoerce err).stdout :: Buffer) 38 | stderr <- Buffer.toString UTF8 ((unsafeCoerce err).stderr :: Buffer) 39 | Console.log stdout 40 | Console.error stderr 41 | throwException err 42 | ) 43 | (ChildProcess.execSync' genCmd opts) 44 | 45 | let 46 | header = 47 | [ "--------------------------------------------" 48 | , "-- This module is generated. DO NOT EDIT! --" 49 | , "--------------------------------------------" 50 | , "module Tidy.Operators.Defaults where" 51 | , "" 52 | , "defaultOperators :: Array String" 53 | , "defaultOperators =" 54 | ] 55 | 56 | lines = output # String.trim # String.split (Pattern "\n") # mapWithIndex \ix line -> 57 | if ix == 0 then 58 | " [ \"\"\"" <> line <> "\"\"\"" 59 | else 60 | " , \"\"\"" <> line <> "\"\"\"" 61 | 62 | footer = 63 | [ " ]" 64 | , "" 65 | ] 66 | 67 | contents = 68 | Array.intercalate "\n" (header <> lines <> footer) 69 | 70 | writeTextFile UTF8 (Path.concat [ cwdPath, "src", "Tidy", "Operators", "Defaults.purs" ]) contents 71 | 72 | defaultPackageJson :: String 73 | defaultPackageJson = 74 | """ 75 | { 76 | "private": true, 77 | "type": "module", 78 | "dependencies": { 79 | "purescript": "^0.15.15", 80 | "spago": "next" 81 | }, 82 | "scripts": { 83 | "postinstall": "spago install" 84 | } 85 | } 86 | """ 87 | 88 | defaultSpagoYaml :: String 89 | defaultSpagoYaml = 90 | """ 91 | package: 92 | name: tidy-generate-default-operators 93 | dependencies: 94 | - ace 95 | - aff 96 | - aff-bus 97 | - aff-coroutines 98 | - affjax 99 | - argonaut 100 | - argonaut-codecs 101 | - argonaut-core 102 | - argonaut-generic 103 | - argonaut-traversals 104 | - arraybuffer-types 105 | - arrays 106 | - assert 107 | - avar 108 | - bifunctors 109 | - catenable-lists 110 | - concurrent-queues 111 | - console 112 | - const 113 | - contravariant 114 | - control 115 | - coroutines 116 | - datetime 117 | - distributive 118 | - effect 119 | - either 120 | - enums 121 | - exceptions 122 | - exists 123 | - filterable 124 | - fixed-points 125 | - foldable-traversable 126 | - foreign 127 | - foreign-object 128 | - fork 129 | - form-urlencoded 130 | - formatters 131 | - free 132 | - freet 133 | - functions 134 | - functors 135 | - gen 136 | - graphs 137 | - http-methods 138 | - identity 139 | - integers 140 | - invariant 141 | - js-date 142 | - js-timers 143 | - js-uri 144 | - lazy 145 | - lcg 146 | - lists 147 | - machines 148 | - matryoshka 149 | - maybe 150 | - media-types 151 | - minibench 152 | - newtype 153 | - nonempty 154 | - now 155 | - nullable 156 | - numbers 157 | - options 158 | - ordered-collections 159 | - orders 160 | - parallel 161 | - parsing 162 | - partial 163 | - pathy 164 | - precise 165 | - prelude 166 | - profunctor 167 | - profunctor-lenses 168 | - psci-support 169 | - quickcheck 170 | - quickcheck-laws 171 | - random 172 | - react 173 | - react-dom 174 | - record 175 | - refs 176 | - routing 177 | - safe-coerce 178 | - semirings 179 | - st 180 | - string-parsers 181 | - strings 182 | - strings-extra 183 | - tailrec 184 | - these 185 | - transformers 186 | - tuples 187 | - type-equality 188 | - typelevel-prelude 189 | - unfoldable 190 | - unicode 191 | - unsafe-coerce 192 | - unsafe-reference 193 | - uri 194 | - validation 195 | workspace: 196 | packageSet: 197 | registry: 63.1.2 198 | extraPackages: {} 199 | """ 200 | -------------------------------------------------------------------------------- /spago.lock: -------------------------------------------------------------------------------- 1 | { 2 | "workspace": { 3 | "packages": { 4 | "tidy": { 5 | "path": "./", 6 | "core": { 7 | "dependencies": [ 8 | { 9 | "arrays": ">=7.3.0 <8.0.0" 10 | }, 11 | { 12 | "control": ">=6.0.0 <7.0.0" 13 | }, 14 | { 15 | "dodo-printer": ">=2.2.3 <3.0.0" 16 | }, 17 | { 18 | "either": ">=6.1.0 <7.0.0" 19 | }, 20 | { 21 | "foldable-traversable": ">=6.0.0 <7.0.0" 22 | }, 23 | { 24 | "language-cst-parser": ">=0.14.1 <0.15.0" 25 | }, 26 | { 27 | "lists": ">=7.0.0 <8.0.0" 28 | }, 29 | { 30 | "maybe": ">=6.0.0 <7.0.0" 31 | }, 32 | { 33 | "newtype": ">=5.0.0 <6.0.0" 34 | }, 35 | { 36 | "ordered-collections": ">=3.2.0 <4.0.0" 37 | }, 38 | { 39 | "partial": ">=4.0.0 <5.0.0" 40 | }, 41 | { 42 | "prelude": ">=6.0.1 <7.0.0" 43 | }, 44 | { 45 | "strings": ">=6.0.1 <7.0.0" 46 | }, 47 | { 48 | "tuples": ">=7.0.0 <8.0.0" 49 | } 50 | ], 51 | "build_plan": [ 52 | "ansi", 53 | "arrays", 54 | "bifunctors", 55 | "catenable-lists", 56 | "const", 57 | "contravariant", 58 | "control", 59 | "distributive", 60 | "dodo-printer", 61 | "effect", 62 | "either", 63 | "enums", 64 | "exceptions", 65 | "exists", 66 | "foldable-traversable", 67 | "free", 68 | "functions", 69 | "functors", 70 | "gen", 71 | "identity", 72 | "integers", 73 | "invariant", 74 | "language-cst-parser", 75 | "lazy", 76 | "lists", 77 | "maybe", 78 | "newtype", 79 | "nonempty", 80 | "numbers", 81 | "ordered-collections", 82 | "orders", 83 | "partial", 84 | "prelude", 85 | "profunctor", 86 | "refs", 87 | "safe-coerce", 88 | "st", 89 | "strings", 90 | "tailrec", 91 | "transformers", 92 | "tuples", 93 | "type-equality", 94 | "typelevel-prelude", 95 | "unfoldable", 96 | "unsafe-coerce" 97 | ] 98 | }, 99 | "test": { 100 | "dependencies": [ 101 | "argparse-basic", 102 | "node-fs", 103 | "node-glob-basic", 104 | "node-path", 105 | { 106 | "tidy-bin": "*" 107 | } 108 | ], 109 | "build_plan": [ 110 | "aff", 111 | "ansi", 112 | "argonaut-codecs", 113 | "argonaut-core", 114 | "argparse-basic", 115 | "arraybuffer-types", 116 | "arrays", 117 | "avar", 118 | "bifunctors", 119 | "catenable-lists", 120 | "console", 121 | "const", 122 | "contravariant", 123 | "control", 124 | "datetime", 125 | "distributive", 126 | "dodo-printer", 127 | "effect", 128 | "either", 129 | "enums", 130 | "exceptions", 131 | "exists", 132 | "foldable-traversable", 133 | "foreign", 134 | "foreign-object", 135 | "free", 136 | "functions", 137 | "functors", 138 | "gen", 139 | "identity", 140 | "integers", 141 | "invariant", 142 | "js-date", 143 | "language-cst-parser", 144 | "lazy", 145 | "lists", 146 | "maybe", 147 | "newtype", 148 | "node-buffer", 149 | "node-event-emitter", 150 | "node-fs", 151 | "node-glob-basic", 152 | "node-path", 153 | "node-process", 154 | "node-streams", 155 | "node-workerbees", 156 | "nonempty", 157 | "now", 158 | "nullable", 159 | "numbers", 160 | "ordered-collections", 161 | "orders", 162 | "parallel", 163 | "partial", 164 | "posix-types", 165 | "prelude", 166 | "profunctor", 167 | "record", 168 | "refs", 169 | "safe-coerce", 170 | "st", 171 | "strings", 172 | "tailrec", 173 | "tidy", 174 | "tidy-bin", 175 | "transformers", 176 | "tuples", 177 | "type-equality", 178 | "typelevel-prelude", 179 | "unfoldable", 180 | "unsafe-coerce", 181 | "variant" 182 | ] 183 | } 184 | }, 185 | "tidy-bin": { 186 | "path": "bin", 187 | "core": { 188 | "dependencies": [ 189 | { 190 | "aff": ">=8.0.0 <9.0.0" 191 | }, 192 | { 193 | "argonaut-codecs": ">=9.1.0 <10.0.0" 194 | }, 195 | { 196 | "argonaut-core": ">=7.0.0 <8.0.0" 197 | }, 198 | { 199 | "argparse-basic": ">=2.0.0 <3.0.0" 200 | }, 201 | { 202 | "arrays": ">=7.3.0 <8.0.0" 203 | }, 204 | { 205 | "console": ">=6.1.0 <7.0.0" 206 | }, 207 | { 208 | "control": ">=6.0.0 <7.0.0" 209 | }, 210 | { 211 | "datetime": ">=6.1.0 <7.0.0" 212 | }, 213 | { 214 | "dodo-printer": ">=2.2.3 <3.0.0" 215 | }, 216 | { 217 | "effect": ">=4.0.0 <5.0.0" 218 | }, 219 | { 220 | "either": ">=6.1.0 <7.0.0" 221 | }, 222 | { 223 | "foldable-traversable": ">=6.0.0 <7.0.0" 224 | }, 225 | { 226 | "foreign-object": ">=4.1.0 <5.0.0" 227 | }, 228 | { 229 | "language-cst-parser": ">=0.14.1 <0.15.0" 230 | }, 231 | { 232 | "lazy": ">=6.0.0 <7.0.0" 233 | }, 234 | { 235 | "lists": ">=7.0.0 <8.0.0" 236 | }, 237 | { 238 | "maybe": ">=6.0.0 <7.0.0" 239 | }, 240 | { 241 | "newtype": ">=5.0.0 <6.0.0" 242 | }, 243 | { 244 | "node-buffer": ">=9.0.0 <10.0.0" 245 | }, 246 | { 247 | "node-event-emitter": ">=3.0.0 <4.0.0" 248 | }, 249 | { 250 | "node-fs": ">=9.2.0 <10.0.0" 251 | }, 252 | { 253 | "node-glob-basic": ">=2.0.0 <3.0.0" 254 | }, 255 | { 256 | "node-path": ">=5.0.1 <6.0.0" 257 | }, 258 | { 259 | "node-process": ">=11.2.0 <12.0.0" 260 | }, 261 | { 262 | "node-streams": ">=9.0.1 <10.0.0" 263 | }, 264 | { 265 | "node-workerbees": ">=0.3.1 <0.4.0" 266 | }, 267 | { 268 | "numbers": ">=9.0.1 <10.0.0" 269 | }, 270 | { 271 | "ordered-collections": ">=3.2.0 <4.0.0" 272 | }, 273 | { 274 | "parallel": ">=7.0.0 <8.0.0" 275 | }, 276 | { 277 | "partial": ">=4.0.0 <5.0.0" 278 | }, 279 | { 280 | "prelude": ">=6.0.1 <7.0.0" 281 | }, 282 | { 283 | "refs": ">=6.0.0 <7.0.0" 284 | }, 285 | { 286 | "strings": ">=6.0.1 <7.0.0" 287 | }, 288 | { 289 | "tidy": "*" 290 | }, 291 | { 292 | "transformers": ">=6.1.0 <7.0.0" 293 | }, 294 | { 295 | "tuples": ">=7.0.0 <8.0.0" 296 | } 297 | ], 298 | "build_plan": [ 299 | "aff", 300 | "ansi", 301 | "argonaut-codecs", 302 | "argonaut-core", 303 | "argparse-basic", 304 | "arraybuffer-types", 305 | "arrays", 306 | "avar", 307 | "bifunctors", 308 | "catenable-lists", 309 | "console", 310 | "const", 311 | "contravariant", 312 | "control", 313 | "datetime", 314 | "distributive", 315 | "dodo-printer", 316 | "effect", 317 | "either", 318 | "enums", 319 | "exceptions", 320 | "exists", 321 | "foldable-traversable", 322 | "foreign", 323 | "foreign-object", 324 | "free", 325 | "functions", 326 | "functors", 327 | "gen", 328 | "identity", 329 | "integers", 330 | "invariant", 331 | "js-date", 332 | "language-cst-parser", 333 | "lazy", 334 | "lists", 335 | "maybe", 336 | "newtype", 337 | "node-buffer", 338 | "node-event-emitter", 339 | "node-fs", 340 | "node-glob-basic", 341 | "node-path", 342 | "node-process", 343 | "node-streams", 344 | "node-workerbees", 345 | "nonempty", 346 | "now", 347 | "nullable", 348 | "numbers", 349 | "ordered-collections", 350 | "orders", 351 | "parallel", 352 | "partial", 353 | "posix-types", 354 | "prelude", 355 | "profunctor", 356 | "record", 357 | "refs", 358 | "safe-coerce", 359 | "st", 360 | "strings", 361 | "tailrec", 362 | "tidy", 363 | "transformers", 364 | "tuples", 365 | "type-equality", 366 | "typelevel-prelude", 367 | "unfoldable", 368 | "unsafe-coerce", 369 | "variant" 370 | ] 371 | }, 372 | "test": { 373 | "dependencies": [], 374 | "build_plan": [] 375 | } 376 | }, 377 | "tidy-script": { 378 | "path": "script", 379 | "core": { 380 | "dependencies": [ 381 | { 382 | "arrays": ">=7.3.0 <8.0.0" 383 | }, 384 | { 385 | "console": ">=6.1.0 <7.0.0" 386 | }, 387 | { 388 | "effect": ">=4.0.0 <5.0.0" 389 | }, 390 | { 391 | "exceptions": ">=6.1.0 <7.0.0" 392 | }, 393 | { 394 | "maybe": ">=6.0.0 <7.0.0" 395 | }, 396 | { 397 | "node-buffer": ">=9.0.0 <10.0.0" 398 | }, 399 | { 400 | "node-child-process": ">=11.1.0 <12.0.0" 401 | }, 402 | { 403 | "node-fs": ">=9.2.0 <10.0.0" 404 | }, 405 | { 406 | "node-path": ">=5.0.1 <6.0.0" 407 | }, 408 | { 409 | "node-process": ">=11.2.0 <12.0.0" 410 | }, 411 | { 412 | "prelude": ">=6.0.1 <7.0.0" 413 | }, 414 | { 415 | "strings": ">=6.0.1 <7.0.0" 416 | }, 417 | { 418 | "unsafe-coerce": ">=6.0.0 <7.0.0" 419 | } 420 | ], 421 | "build_plan": [ 422 | "aff", 423 | "arraybuffer-types", 424 | "arrays", 425 | "bifunctors", 426 | "console", 427 | "const", 428 | "contravariant", 429 | "control", 430 | "datetime", 431 | "distributive", 432 | "effect", 433 | "either", 434 | "enums", 435 | "exceptions", 436 | "exists", 437 | "foldable-traversable", 438 | "foreign", 439 | "foreign-object", 440 | "functions", 441 | "functors", 442 | "gen", 443 | "identity", 444 | "integers", 445 | "invariant", 446 | "js-date", 447 | "lazy", 448 | "lists", 449 | "maybe", 450 | "newtype", 451 | "node-buffer", 452 | "node-child-process", 453 | "node-event-emitter", 454 | "node-fs", 455 | "node-os", 456 | "node-path", 457 | "node-process", 458 | "node-streams", 459 | "nonempty", 460 | "now", 461 | "nullable", 462 | "numbers", 463 | "ordered-collections", 464 | "orders", 465 | "parallel", 466 | "partial", 467 | "posix-types", 468 | "prelude", 469 | "profunctor", 470 | "refs", 471 | "safe-coerce", 472 | "st", 473 | "strings", 474 | "tailrec", 475 | "transformers", 476 | "tuples", 477 | "type-equality", 478 | "typelevel-prelude", 479 | "unfoldable", 480 | "unsafe-coerce" 481 | ] 482 | }, 483 | "test": { 484 | "dependencies": [], 485 | "build_plan": [] 486 | } 487 | } 488 | }, 489 | "extra_packages": {} 490 | }, 491 | "packages": { 492 | "aff": { 493 | "type": "registry", 494 | "version": "8.0.0", 495 | "integrity": "sha256-5MmdI4+0RHBtSBy+YlU3/Cq4R5W2ih3OaRedJIrVHdk=", 496 | "dependencies": [ 497 | "bifunctors", 498 | "control", 499 | "datetime", 500 | "effect", 501 | "either", 502 | "exceptions", 503 | "foldable-traversable", 504 | "functions", 505 | "maybe", 506 | "newtype", 507 | "parallel", 508 | "prelude", 509 | "refs", 510 | "tailrec", 511 | "transformers", 512 | "unsafe-coerce" 513 | ] 514 | }, 515 | "ansi": { 516 | "type": "registry", 517 | "version": "7.0.0", 518 | "integrity": "sha256-ZMB6HD+q9CXvn9fRCmJ8dvuDrOVHcjombL3oNOerVnE=", 519 | "dependencies": [ 520 | "foldable-traversable", 521 | "lists", 522 | "strings" 523 | ] 524 | }, 525 | "argonaut-codecs": { 526 | "type": "registry", 527 | "version": "9.1.0", 528 | "integrity": "sha256-N6efXByUeg848ompEqJfVvZuZPfdRYDGlTDFn0G0Oh8=", 529 | "dependencies": [ 530 | "argonaut-core", 531 | "arrays", 532 | "effect", 533 | "foreign-object", 534 | "identity", 535 | "integers", 536 | "maybe", 537 | "nonempty", 538 | "ordered-collections", 539 | "prelude", 540 | "record" 541 | ] 542 | }, 543 | "argonaut-core": { 544 | "type": "registry", 545 | "version": "7.0.0", 546 | "integrity": "sha256-RC82GfAjItydxrO24cdX373KHVZiLqybu19b5X8u7B4=", 547 | "dependencies": [ 548 | "arrays", 549 | "control", 550 | "either", 551 | "foreign-object", 552 | "functions", 553 | "gen", 554 | "maybe", 555 | "nonempty", 556 | "prelude", 557 | "strings", 558 | "tailrec" 559 | ] 560 | }, 561 | "argparse-basic": { 562 | "type": "registry", 563 | "version": "2.0.0", 564 | "integrity": "sha256-3Pp8MDfRL2oeBdfxXIUVJBdhGniaahw2UOG4QmHPm58=", 565 | "dependencies": [ 566 | "arrays", 567 | "bifunctors", 568 | "control", 569 | "either", 570 | "foldable-traversable", 571 | "integers", 572 | "lists", 573 | "maybe", 574 | "newtype", 575 | "numbers", 576 | "prelude", 577 | "record", 578 | "strings", 579 | "tuples", 580 | "unfoldable" 581 | ] 582 | }, 583 | "arraybuffer-types": { 584 | "type": "registry", 585 | "version": "3.0.2", 586 | "integrity": "sha256-mQKokysYVkooS4uXbO+yovmV/s8b138Ws3zQvOwIHRA=", 587 | "dependencies": [] 588 | }, 589 | "arrays": { 590 | "type": "registry", 591 | "version": "7.3.0", 592 | "integrity": "sha256-tmcklBlc/muUtUfr9RapdCPwnlQeB3aSrC4dK85gQlc=", 593 | "dependencies": [ 594 | "bifunctors", 595 | "control", 596 | "foldable-traversable", 597 | "functions", 598 | "maybe", 599 | "nonempty", 600 | "partial", 601 | "prelude", 602 | "safe-coerce", 603 | "st", 604 | "tailrec", 605 | "tuples", 606 | "unfoldable", 607 | "unsafe-coerce" 608 | ] 609 | }, 610 | "avar": { 611 | "type": "registry", 612 | "version": "5.0.1", 613 | "integrity": "sha256-f+bRR3qQPa/GVe4UbLQiJBy7+PzJkUCwT6qNn0UlkMY=", 614 | "dependencies": [ 615 | "aff", 616 | "effect", 617 | "either", 618 | "exceptions", 619 | "functions", 620 | "maybe" 621 | ] 622 | }, 623 | "bifunctors": { 624 | "type": "registry", 625 | "version": "6.0.0", 626 | "integrity": "sha256-/gZwC9YhNxZNQpnHa5BIYerCGM2jeX9ukZiEvYxm5Nw=", 627 | "dependencies": [ 628 | "const", 629 | "either", 630 | "newtype", 631 | "prelude", 632 | "tuples" 633 | ] 634 | }, 635 | "catenable-lists": { 636 | "type": "registry", 637 | "version": "7.0.0", 638 | "integrity": "sha256-76vYENhwF4BWTBsjeLuErCH2jqVT4M3R1HX+4RwSftA=", 639 | "dependencies": [ 640 | "control", 641 | "foldable-traversable", 642 | "lists", 643 | "maybe", 644 | "prelude", 645 | "tuples", 646 | "unfoldable" 647 | ] 648 | }, 649 | "console": { 650 | "type": "registry", 651 | "version": "6.1.0", 652 | "integrity": "sha256-CxmAzjgyuGDmt9FZW51VhV6rBPwR6o0YeKUzA9rSzcM=", 653 | "dependencies": [ 654 | "effect", 655 | "prelude" 656 | ] 657 | }, 658 | "const": { 659 | "type": "registry", 660 | "version": "6.0.0", 661 | "integrity": "sha256-tNrxDW8D8H4jdHE2HiPzpLy08zkzJMmGHdRqt5BQuTc=", 662 | "dependencies": [ 663 | "invariant", 664 | "newtype", 665 | "prelude" 666 | ] 667 | }, 668 | "contravariant": { 669 | "type": "registry", 670 | "version": "6.0.0", 671 | "integrity": "sha256-TP+ooAp3vvmdjfQsQJSichF5B4BPDHp3wAJoWchip6c=", 672 | "dependencies": [ 673 | "const", 674 | "either", 675 | "newtype", 676 | "prelude", 677 | "tuples" 678 | ] 679 | }, 680 | "control": { 681 | "type": "registry", 682 | "version": "6.0.0", 683 | "integrity": "sha256-sH7Pg9E96JCPF9PIA6oQ8+BjTyO/BH1ZuE/bOcyj4Jk=", 684 | "dependencies": [ 685 | "newtype", 686 | "prelude" 687 | ] 688 | }, 689 | "datetime": { 690 | "type": "registry", 691 | "version": "6.1.0", 692 | "integrity": "sha256-g/5X5BBegQWLpI9IWD+sY6mcaYpzzlW5lz5NBzaMtyI=", 693 | "dependencies": [ 694 | "bifunctors", 695 | "control", 696 | "either", 697 | "enums", 698 | "foldable-traversable", 699 | "functions", 700 | "gen", 701 | "integers", 702 | "lists", 703 | "maybe", 704 | "newtype", 705 | "numbers", 706 | "ordered-collections", 707 | "partial", 708 | "prelude", 709 | "tuples" 710 | ] 711 | }, 712 | "distributive": { 713 | "type": "registry", 714 | "version": "6.0.0", 715 | "integrity": "sha256-HTDdmEnzigMl+02SJB88j+gAXDx9VKsbvR4MJGDPbOQ=", 716 | "dependencies": [ 717 | "identity", 718 | "newtype", 719 | "prelude", 720 | "tuples", 721 | "type-equality" 722 | ] 723 | }, 724 | "dodo-printer": { 725 | "type": "registry", 726 | "version": "2.2.3", 727 | "integrity": "sha256-+XQtWgt+ybwvQb+QbJ60wm4/hxGRAQoSmeR+Se+ZT7I=", 728 | "dependencies": [ 729 | "ansi", 730 | "either", 731 | "foldable-traversable", 732 | "integers", 733 | "lists", 734 | "maybe", 735 | "newtype", 736 | "partial", 737 | "prelude", 738 | "safe-coerce", 739 | "strings", 740 | "tuples" 741 | ] 742 | }, 743 | "effect": { 744 | "type": "registry", 745 | "version": "4.0.0", 746 | "integrity": "sha256-eBtZu+HZcMa5HilvI6kaDyVX3ji8p0W9MGKy2K4T6+M=", 747 | "dependencies": [ 748 | "prelude" 749 | ] 750 | }, 751 | "either": { 752 | "type": "registry", 753 | "version": "6.1.0", 754 | "integrity": "sha256-6hgTPisnMWVwQivOu2PKYcH8uqjEOOqDyaDQVUchTpY=", 755 | "dependencies": [ 756 | "control", 757 | "invariant", 758 | "maybe", 759 | "prelude" 760 | ] 761 | }, 762 | "enums": { 763 | "type": "registry", 764 | "version": "6.0.1", 765 | "integrity": "sha256-HWaD73JFLorc4A6trKIRUeDMdzE+GpkJaEOM1nTNkC8=", 766 | "dependencies": [ 767 | "control", 768 | "either", 769 | "gen", 770 | "maybe", 771 | "newtype", 772 | "nonempty", 773 | "partial", 774 | "prelude", 775 | "tuples", 776 | "unfoldable" 777 | ] 778 | }, 779 | "exceptions": { 780 | "type": "registry", 781 | "version": "6.1.0", 782 | "integrity": "sha256-K0T89IHtF3vBY7eSAO7eDOqSb2J9kZGAcDN5+IKsF8E=", 783 | "dependencies": [ 784 | "effect", 785 | "either", 786 | "maybe", 787 | "prelude" 788 | ] 789 | }, 790 | "exists": { 791 | "type": "registry", 792 | "version": "6.0.0", 793 | "integrity": "sha256-A0JQHpTfo1dNOj9U5/Fd3xndlRSE0g2IQWOGor2yXn8=", 794 | "dependencies": [ 795 | "unsafe-coerce" 796 | ] 797 | }, 798 | "foldable-traversable": { 799 | "type": "registry", 800 | "version": "6.0.0", 801 | "integrity": "sha256-fLeqRYM4jUrZD5H4WqcwUgzU7XfYkzO4zhgtNc3jcWM=", 802 | "dependencies": [ 803 | "bifunctors", 804 | "const", 805 | "control", 806 | "either", 807 | "functors", 808 | "identity", 809 | "maybe", 810 | "newtype", 811 | "orders", 812 | "prelude", 813 | "tuples" 814 | ] 815 | }, 816 | "foreign": { 817 | "type": "registry", 818 | "version": "7.0.0", 819 | "integrity": "sha256-1ORiqoS3HW+qfwSZAppHPWy4/6AQysxZ2t29jcdUMNA=", 820 | "dependencies": [ 821 | "either", 822 | "functions", 823 | "identity", 824 | "integers", 825 | "lists", 826 | "maybe", 827 | "prelude", 828 | "strings", 829 | "transformers" 830 | ] 831 | }, 832 | "foreign-object": { 833 | "type": "registry", 834 | "version": "4.1.0", 835 | "integrity": "sha256-q24okj6mT+yGHYQ+ei/pYPj5ih6sTbu7eDv/WU56JVo=", 836 | "dependencies": [ 837 | "arrays", 838 | "foldable-traversable", 839 | "functions", 840 | "gen", 841 | "lists", 842 | "maybe", 843 | "prelude", 844 | "st", 845 | "tailrec", 846 | "tuples", 847 | "typelevel-prelude", 848 | "unfoldable" 849 | ] 850 | }, 851 | "free": { 852 | "type": "registry", 853 | "version": "7.1.0", 854 | "integrity": "sha256-JAumgEsGSzJCNLD8AaFvuX7CpqS5yruCngi6yI7+V5k=", 855 | "dependencies": [ 856 | "catenable-lists", 857 | "control", 858 | "distributive", 859 | "either", 860 | "exists", 861 | "foldable-traversable", 862 | "invariant", 863 | "lazy", 864 | "maybe", 865 | "prelude", 866 | "tailrec", 867 | "transformers", 868 | "tuples", 869 | "unsafe-coerce" 870 | ] 871 | }, 872 | "functions": { 873 | "type": "registry", 874 | "version": "6.0.0", 875 | "integrity": "sha256-adMyJNEnhGde2unHHAP79gPtlNjNqzgLB8arEOn9hLI=", 876 | "dependencies": [ 877 | "prelude" 878 | ] 879 | }, 880 | "functors": { 881 | "type": "registry", 882 | "version": "5.0.0", 883 | "integrity": "sha256-zfPWWYisbD84MqwpJSZFlvM6v86McM68ob8p9s27ywU=", 884 | "dependencies": [ 885 | "bifunctors", 886 | "const", 887 | "contravariant", 888 | "control", 889 | "distributive", 890 | "either", 891 | "invariant", 892 | "maybe", 893 | "newtype", 894 | "prelude", 895 | "profunctor", 896 | "tuples", 897 | "unsafe-coerce" 898 | ] 899 | }, 900 | "gen": { 901 | "type": "registry", 902 | "version": "4.0.0", 903 | "integrity": "sha256-f7yzAXWwr+xnaqEOcvyO3ezKdoes8+WXWdXIHDBCAPI=", 904 | "dependencies": [ 905 | "either", 906 | "foldable-traversable", 907 | "identity", 908 | "maybe", 909 | "newtype", 910 | "nonempty", 911 | "prelude", 912 | "tailrec", 913 | "tuples", 914 | "unfoldable" 915 | ] 916 | }, 917 | "identity": { 918 | "type": "registry", 919 | "version": "6.0.0", 920 | "integrity": "sha256-4wY0XZbAksjY6UAg99WkuKyJlQlWAfTi2ssadH0wVMY=", 921 | "dependencies": [ 922 | "control", 923 | "invariant", 924 | "newtype", 925 | "prelude" 926 | ] 927 | }, 928 | "integers": { 929 | "type": "registry", 930 | "version": "6.0.0", 931 | "integrity": "sha256-sf+sK26R1hzwl3NhXR7WAu9zCDjQnfoXwcyGoseX158=", 932 | "dependencies": [ 933 | "maybe", 934 | "numbers", 935 | "prelude" 936 | ] 937 | }, 938 | "invariant": { 939 | "type": "registry", 940 | "version": "6.0.0", 941 | "integrity": "sha256-RGWWyYrz0Hs1KjPDA+87Kia67ZFBhfJ5lMGOMCEFoLo=", 942 | "dependencies": [ 943 | "control", 944 | "prelude" 945 | ] 946 | }, 947 | "js-date": { 948 | "type": "registry", 949 | "version": "8.0.0", 950 | "integrity": "sha256-6TVF4DWg5JL+jRAsoMssYw8rgOVALMUHT1CuNZt8NRo=", 951 | "dependencies": [ 952 | "datetime", 953 | "effect", 954 | "exceptions", 955 | "foreign", 956 | "integers", 957 | "now" 958 | ] 959 | }, 960 | "language-cst-parser": { 961 | "type": "registry", 962 | "version": "0.14.1", 963 | "integrity": "sha256-LJzh1ZTaKjcrHx95ZfO2La3w6Xb/IZQT3m2Nuj4n1dM=", 964 | "dependencies": [ 965 | "arrays", 966 | "const", 967 | "control", 968 | "effect", 969 | "either", 970 | "enums", 971 | "foldable-traversable", 972 | "free", 973 | "functions", 974 | "functors", 975 | "identity", 976 | "integers", 977 | "lazy", 978 | "lists", 979 | "maybe", 980 | "newtype", 981 | "numbers", 982 | "ordered-collections", 983 | "partial", 984 | "prelude", 985 | "st", 986 | "strings", 987 | "transformers", 988 | "tuples", 989 | "typelevel-prelude", 990 | "unfoldable", 991 | "unsafe-coerce" 992 | ] 993 | }, 994 | "lazy": { 995 | "type": "registry", 996 | "version": "6.0.0", 997 | "integrity": "sha256-lMsfFOnlqfe4KzRRiW8ot5ge6HtcU3Eyh2XkXcP5IgU=", 998 | "dependencies": [ 999 | "control", 1000 | "foldable-traversable", 1001 | "invariant", 1002 | "prelude" 1003 | ] 1004 | }, 1005 | "lists": { 1006 | "type": "registry", 1007 | "version": "7.0.0", 1008 | "integrity": "sha256-EKF15qYqucuXP2lT/xPxhqy58f0FFT6KHdIB/yBOayI=", 1009 | "dependencies": [ 1010 | "bifunctors", 1011 | "control", 1012 | "foldable-traversable", 1013 | "lazy", 1014 | "maybe", 1015 | "newtype", 1016 | "nonempty", 1017 | "partial", 1018 | "prelude", 1019 | "tailrec", 1020 | "tuples", 1021 | "unfoldable" 1022 | ] 1023 | }, 1024 | "maybe": { 1025 | "type": "registry", 1026 | "version": "6.0.0", 1027 | "integrity": "sha256-5cCIb0wPwbat2PRkQhUeZO0jcAmf8jCt2qE0wbC3v2Q=", 1028 | "dependencies": [ 1029 | "control", 1030 | "invariant", 1031 | "newtype", 1032 | "prelude" 1033 | ] 1034 | }, 1035 | "newtype": { 1036 | "type": "registry", 1037 | "version": "5.0.0", 1038 | "integrity": "sha256-gdrQu8oGe9eZE6L3wOI8ql/igOg+zEGB5ITh2g+uttw=", 1039 | "dependencies": [ 1040 | "prelude", 1041 | "safe-coerce" 1042 | ] 1043 | }, 1044 | "node-buffer": { 1045 | "type": "registry", 1046 | "version": "9.0.0", 1047 | "integrity": "sha256-PWE2DJ5ruBLCmeA/fUiuySEFmUJ/VuRfyrnCuVZBlu4=", 1048 | "dependencies": [ 1049 | "arraybuffer-types", 1050 | "effect", 1051 | "maybe", 1052 | "nullable", 1053 | "st", 1054 | "unsafe-coerce" 1055 | ] 1056 | }, 1057 | "node-child-process": { 1058 | "type": "registry", 1059 | "version": "11.1.0", 1060 | "integrity": "sha256-vioMNgk8p+CGwlb6T3I3TIir27el85Yg4satLE/I89w=", 1061 | "dependencies": [ 1062 | "exceptions", 1063 | "foreign", 1064 | "foreign-object", 1065 | "functions", 1066 | "node-event-emitter", 1067 | "node-fs", 1068 | "node-os", 1069 | "node-streams", 1070 | "nullable", 1071 | "posix-types", 1072 | "unsafe-coerce" 1073 | ] 1074 | }, 1075 | "node-event-emitter": { 1076 | "type": "registry", 1077 | "version": "3.0.0", 1078 | "integrity": "sha256-Qw0MjsT4xRH2j2i4K8JmRjcMKnH5z1Cw39t00q4LE4w=", 1079 | "dependencies": [ 1080 | "effect", 1081 | "either", 1082 | "functions", 1083 | "maybe", 1084 | "nullable", 1085 | "prelude", 1086 | "unsafe-coerce" 1087 | ] 1088 | }, 1089 | "node-fs": { 1090 | "type": "registry", 1091 | "version": "9.2.0", 1092 | "integrity": "sha256-Sg0vkXycEzkEerX6hLccz21Ygd9w1+QSk1thotRZPGI=", 1093 | "dependencies": [ 1094 | "datetime", 1095 | "effect", 1096 | "either", 1097 | "enums", 1098 | "exceptions", 1099 | "functions", 1100 | "integers", 1101 | "js-date", 1102 | "maybe", 1103 | "node-buffer", 1104 | "node-path", 1105 | "node-streams", 1106 | "nullable", 1107 | "partial", 1108 | "prelude", 1109 | "strings", 1110 | "unsafe-coerce" 1111 | ] 1112 | }, 1113 | "node-glob-basic": { 1114 | "type": "registry", 1115 | "version": "2.0.0", 1116 | "integrity": "sha256-COPsHFpqwFGOBbo3DZ4yQLp5UTNshLcINIV2P3h8g6o=", 1117 | "dependencies": [ 1118 | "aff", 1119 | "effect", 1120 | "either", 1121 | "foldable-traversable", 1122 | "lists", 1123 | "maybe", 1124 | "node-fs", 1125 | "node-path", 1126 | "node-process", 1127 | "ordered-collections", 1128 | "parallel", 1129 | "prelude", 1130 | "refs", 1131 | "strings", 1132 | "tuples" 1133 | ] 1134 | }, 1135 | "node-os": { 1136 | "type": "registry", 1137 | "version": "5.1.0", 1138 | "integrity": "sha256-K3gcu9AXanN1+qtk1900+Fi+CuO0s3/H/RMNRNgIzso=", 1139 | "dependencies": [ 1140 | "arrays", 1141 | "bifunctors", 1142 | "console", 1143 | "control", 1144 | "datetime", 1145 | "effect", 1146 | "either", 1147 | "exceptions", 1148 | "foldable-traversable", 1149 | "foreign", 1150 | "foreign-object", 1151 | "functions", 1152 | "maybe", 1153 | "node-buffer", 1154 | "nullable", 1155 | "partial", 1156 | "posix-types", 1157 | "prelude", 1158 | "unsafe-coerce" 1159 | ] 1160 | }, 1161 | "node-path": { 1162 | "type": "registry", 1163 | "version": "5.0.1", 1164 | "integrity": "sha256-ePOElFamHkffhwJcS0Ozq4A14rflnkasFU6X2B8/yXs=", 1165 | "dependencies": [ 1166 | "effect" 1167 | ] 1168 | }, 1169 | "node-process": { 1170 | "type": "registry", 1171 | "version": "11.2.0", 1172 | "integrity": "sha256-+2MQDYChjGbVbapCyJtuWYwD41jk+BntF/kcOTKBMVs=", 1173 | "dependencies": [ 1174 | "effect", 1175 | "foreign", 1176 | "foreign-object", 1177 | "maybe", 1178 | "node-event-emitter", 1179 | "node-streams", 1180 | "posix-types", 1181 | "prelude", 1182 | "unsafe-coerce" 1183 | ] 1184 | }, 1185 | "node-streams": { 1186 | "type": "registry", 1187 | "version": "9.0.1", 1188 | "integrity": "sha256-7RJ6RqjOlhW+QlDFQNUHlkCG/CuYTTLT8yary5jhhsU=", 1189 | "dependencies": [ 1190 | "aff", 1191 | "arrays", 1192 | "effect", 1193 | "either", 1194 | "exceptions", 1195 | "maybe", 1196 | "node-buffer", 1197 | "node-event-emitter", 1198 | "nullable", 1199 | "prelude", 1200 | "refs", 1201 | "st", 1202 | "tailrec", 1203 | "unsafe-coerce" 1204 | ] 1205 | }, 1206 | "node-workerbees": { 1207 | "type": "registry", 1208 | "version": "0.3.1", 1209 | "integrity": "sha256-EoyDKWQpD9Wc/j/068HcOcQ8F/+2KVX+7F+RT+gNq3Y=", 1210 | "dependencies": [ 1211 | "aff", 1212 | "argonaut-core", 1213 | "arraybuffer-types", 1214 | "arrays", 1215 | "avar", 1216 | "effect", 1217 | "either", 1218 | "exceptions", 1219 | "foldable-traversable", 1220 | "foreign-object", 1221 | "newtype", 1222 | "parallel", 1223 | "prelude", 1224 | "transformers", 1225 | "tuples", 1226 | "variant" 1227 | ] 1228 | }, 1229 | "nonempty": { 1230 | "type": "registry", 1231 | "version": "7.0.0", 1232 | "integrity": "sha256-54ablJZUHGvvlTJzi3oXyPCuvY6zsrWJuH/dMJ/MFLs=", 1233 | "dependencies": [ 1234 | "control", 1235 | "foldable-traversable", 1236 | "maybe", 1237 | "prelude", 1238 | "tuples", 1239 | "unfoldable" 1240 | ] 1241 | }, 1242 | "now": { 1243 | "type": "registry", 1244 | "version": "6.0.0", 1245 | "integrity": "sha256-xZ7x37ZMREfs6GCDw/h+FaKHV/3sPWmtqBZRGTxybQY=", 1246 | "dependencies": [ 1247 | "datetime", 1248 | "effect" 1249 | ] 1250 | }, 1251 | "nullable": { 1252 | "type": "registry", 1253 | "version": "6.0.0", 1254 | "integrity": "sha256-yiGBVl3AD+Guy4kNWWeN+zl1gCiJK+oeIFtZtPCw4+o=", 1255 | "dependencies": [ 1256 | "effect", 1257 | "functions", 1258 | "maybe" 1259 | ] 1260 | }, 1261 | "numbers": { 1262 | "type": "registry", 1263 | "version": "9.0.1", 1264 | "integrity": "sha256-/9M6aeMDBdB4cwYDeJvLFprAHZ49EbtKQLIJsneXLIk=", 1265 | "dependencies": [ 1266 | "functions", 1267 | "maybe" 1268 | ] 1269 | }, 1270 | "ordered-collections": { 1271 | "type": "registry", 1272 | "version": "3.2.0", 1273 | "integrity": "sha256-o9jqsj5rpJmMdoe/zyufWHFjYYFTTsJpgcuCnqCO6PM=", 1274 | "dependencies": [ 1275 | "arrays", 1276 | "foldable-traversable", 1277 | "gen", 1278 | "lists", 1279 | "maybe", 1280 | "partial", 1281 | "prelude", 1282 | "st", 1283 | "tailrec", 1284 | "tuples", 1285 | "unfoldable" 1286 | ] 1287 | }, 1288 | "orders": { 1289 | "type": "registry", 1290 | "version": "6.0.0", 1291 | "integrity": "sha256-nBA0g3/ai0euH8q9pSbGqk53W2q6agm/dECZTHcoink=", 1292 | "dependencies": [ 1293 | "newtype", 1294 | "prelude" 1295 | ] 1296 | }, 1297 | "parallel": { 1298 | "type": "registry", 1299 | "version": "7.0.0", 1300 | "integrity": "sha256-gUC9i4Txnx9K9RcMLsjujbwZz6BB1bnE2MLvw4GIw5o=", 1301 | "dependencies": [ 1302 | "control", 1303 | "effect", 1304 | "either", 1305 | "foldable-traversable", 1306 | "functors", 1307 | "maybe", 1308 | "newtype", 1309 | "prelude", 1310 | "profunctor", 1311 | "refs", 1312 | "transformers" 1313 | ] 1314 | }, 1315 | "partial": { 1316 | "type": "registry", 1317 | "version": "4.0.0", 1318 | "integrity": "sha256-fwXerld6Xw1VkReh8yeQsdtLVrjfGiVuC5bA1Wyo/J4=", 1319 | "dependencies": [] 1320 | }, 1321 | "posix-types": { 1322 | "type": "registry", 1323 | "version": "6.0.0", 1324 | "integrity": "sha256-ZfFz8RR1lee/o/Prccyeut3Q+9tYd08mlR72sIh6GzA=", 1325 | "dependencies": [ 1326 | "maybe", 1327 | "prelude" 1328 | ] 1329 | }, 1330 | "prelude": { 1331 | "type": "registry", 1332 | "version": "6.0.1", 1333 | "integrity": "sha256-o8p6SLYmVPqzXZhQFd2hGAWEwBoXl1swxLG/scpJ0V0=", 1334 | "dependencies": [] 1335 | }, 1336 | "profunctor": { 1337 | "type": "registry", 1338 | "version": "6.0.1", 1339 | "integrity": "sha256-E58hSYdJvF2Qjf9dnWLPlJKh2Z2fLfFLkQoYi16vsFk=", 1340 | "dependencies": [ 1341 | "control", 1342 | "distributive", 1343 | "either", 1344 | "exists", 1345 | "invariant", 1346 | "newtype", 1347 | "prelude", 1348 | "tuples" 1349 | ] 1350 | }, 1351 | "record": { 1352 | "type": "registry", 1353 | "version": "4.0.0", 1354 | "integrity": "sha256-Za5U85bTRJEfGK5Sk4hM41oXy84YQI0I8TL3WUn1Qzg=", 1355 | "dependencies": [ 1356 | "functions", 1357 | "prelude", 1358 | "unsafe-coerce" 1359 | ] 1360 | }, 1361 | "refs": { 1362 | "type": "registry", 1363 | "version": "6.0.0", 1364 | "integrity": "sha256-Vgwne7jIbD3ZMoLNNETLT8Litw6lIYo3MfYNdtYWj9s=", 1365 | "dependencies": [ 1366 | "effect", 1367 | "prelude" 1368 | ] 1369 | }, 1370 | "safe-coerce": { 1371 | "type": "registry", 1372 | "version": "2.0.0", 1373 | "integrity": "sha256-a1ibQkiUcbODbLE/WAq7Ttbbh9ex+x33VCQ7GngKudU=", 1374 | "dependencies": [ 1375 | "unsafe-coerce" 1376 | ] 1377 | }, 1378 | "st": { 1379 | "type": "registry", 1380 | "version": "6.2.0", 1381 | "integrity": "sha256-z9X0WsOUlPwNx9GlCC+YccCyz8MejC8Wb0C4+9fiBRY=", 1382 | "dependencies": [ 1383 | "partial", 1384 | "prelude", 1385 | "tailrec", 1386 | "unsafe-coerce" 1387 | ] 1388 | }, 1389 | "strings": { 1390 | "type": "registry", 1391 | "version": "6.0.1", 1392 | "integrity": "sha256-WssD3DbX4OPzxSdjvRMX0yvc9+pS7n5gyPv5I2Trb7k=", 1393 | "dependencies": [ 1394 | "arrays", 1395 | "control", 1396 | "either", 1397 | "enums", 1398 | "foldable-traversable", 1399 | "gen", 1400 | "integers", 1401 | "maybe", 1402 | "newtype", 1403 | "nonempty", 1404 | "partial", 1405 | "prelude", 1406 | "tailrec", 1407 | "tuples", 1408 | "unfoldable", 1409 | "unsafe-coerce" 1410 | ] 1411 | }, 1412 | "tailrec": { 1413 | "type": "registry", 1414 | "version": "6.1.0", 1415 | "integrity": "sha256-Xx19ECVDRrDWpz9D2GxQHHV89vd61dnXxQm0IcYQHGk=", 1416 | "dependencies": [ 1417 | "bifunctors", 1418 | "effect", 1419 | "either", 1420 | "identity", 1421 | "maybe", 1422 | "partial", 1423 | "prelude", 1424 | "refs" 1425 | ] 1426 | }, 1427 | "transformers": { 1428 | "type": "registry", 1429 | "version": "6.1.0", 1430 | "integrity": "sha256-3Bm+Z6tsC/paG888XkywDngJ2JMos+JfOhRlkVfb7gI=", 1431 | "dependencies": [ 1432 | "control", 1433 | "distributive", 1434 | "effect", 1435 | "either", 1436 | "exceptions", 1437 | "foldable-traversable", 1438 | "identity", 1439 | "lazy", 1440 | "maybe", 1441 | "newtype", 1442 | "prelude", 1443 | "st", 1444 | "tailrec", 1445 | "tuples", 1446 | "unfoldable" 1447 | ] 1448 | }, 1449 | "tuples": { 1450 | "type": "registry", 1451 | "version": "7.0.0", 1452 | "integrity": "sha256-1rXgTomes9105BjgXqIw0FL6Fz1lqqUTLWOumhWec1M=", 1453 | "dependencies": [ 1454 | "control", 1455 | "invariant", 1456 | "prelude" 1457 | ] 1458 | }, 1459 | "type-equality": { 1460 | "type": "registry", 1461 | "version": "4.0.1", 1462 | "integrity": "sha256-Hs9D6Y71zFi/b+qu5NSbuadUQXe5iv5iWx0226vOHUw=", 1463 | "dependencies": [] 1464 | }, 1465 | "typelevel-prelude": { 1466 | "type": "registry", 1467 | "version": "7.0.0", 1468 | "integrity": "sha256-uFF2ph+vHcQpfPuPf2a3ukJDFmLhApmkpTMviHIWgJM=", 1469 | "dependencies": [ 1470 | "prelude", 1471 | "type-equality" 1472 | ] 1473 | }, 1474 | "unfoldable": { 1475 | "type": "registry", 1476 | "version": "6.0.0", 1477 | "integrity": "sha256-JtikvJdktRap7vr/K4ITlxUX1QexpnqBq0G/InLr6eg=", 1478 | "dependencies": [ 1479 | "foldable-traversable", 1480 | "maybe", 1481 | "partial", 1482 | "prelude", 1483 | "tuples" 1484 | ] 1485 | }, 1486 | "unsafe-coerce": { 1487 | "type": "registry", 1488 | "version": "6.0.0", 1489 | "integrity": "sha256-IqIYW4Vkevn8sI+6aUwRGvd87tVL36BBeOr0cGAE7t0=", 1490 | "dependencies": [] 1491 | }, 1492 | "variant": { 1493 | "type": "registry", 1494 | "version": "8.0.0", 1495 | "integrity": "sha256-SR//zQDg2dnbB8ZHslcxieUkCeNlbMToapvmh9onTtw=", 1496 | "dependencies": [ 1497 | "enums", 1498 | "lists", 1499 | "maybe", 1500 | "partial", 1501 | "prelude", 1502 | "record", 1503 | "tuples", 1504 | "unsafe-coerce" 1505 | ] 1506 | } 1507 | } 1508 | } 1509 | -------------------------------------------------------------------------------- /spago.yaml: -------------------------------------------------------------------------------- 1 | package: 2 | name: tidy 3 | description: A syntax tidy-upper (formatter) for PureScript. 4 | publish: 5 | version: 0.11.1 6 | license: MIT 7 | location: 8 | githubOwner: natefaubion 9 | githubRepo: purescript-tidy 10 | build: 11 | strict: true 12 | dependencies: 13 | - arrays: ">=7.3.0 <8.0.0" 14 | - control: ">=6.0.0 <7.0.0" 15 | - dodo-printer: ">=2.2.3 <3.0.0" 16 | - either: ">=6.1.0 <7.0.0" 17 | - foldable-traversable: ">=6.0.0 <7.0.0" 18 | - language-cst-parser: ">=0.14.1 <0.15.0" 19 | - lists: ">=7.0.0 <8.0.0" 20 | - maybe: ">=6.0.0 <7.0.0" 21 | - newtype: ">=5.0.0 <6.0.0" 22 | - ordered-collections: ">=3.2.0 <4.0.0" 23 | - partial: ">=4.0.0 <5.0.0" 24 | - prelude: ">=6.0.1 <7.0.0" 25 | - strings: ">=6.0.1 <7.0.0" 26 | - tuples: ">=7.0.0 <8.0.0" 27 | test: 28 | main: Test.Main 29 | dependencies: 30 | - argparse-basic 31 | - node-fs 32 | - node-glob-basic 33 | - node-path 34 | - tidy-bin: "*" 35 | workspace: {} 36 | -------------------------------------------------------------------------------- /src/Tidy/Doc.purs: -------------------------------------------------------------------------------- 1 | module Tidy.Doc 2 | ( FormatDoc(..) 3 | , LeadingComment(..) 4 | , TrailingComment(..) 5 | , ForceBreak(..) 6 | , text 7 | , leadingLineComment 8 | , trailingLineComment 9 | , leadingBlockComment 10 | , trailingBlockComment 11 | , anchor 12 | , flatten 13 | , flattenMax 14 | , indent 15 | , align 16 | , alignCurrentColumn 17 | , locally 18 | , break 19 | , softBreak 20 | , spaceBreak 21 | , sourceBreak 22 | , forceMinSourceBreaks 23 | , space 24 | , softSpace 25 | , flexSpaceBreak 26 | , flexSoftSpace 27 | , flexSoftBreak 28 | , flexDoubleBreak 29 | , flexGroup 30 | , fromDoc 31 | , toDoc 32 | , mapDoc 33 | , breakDoc 34 | , breaks 35 | , joinWithMap 36 | , joinWith 37 | ) where 38 | 39 | import Prelude 40 | 41 | import Control.Alternative (guard) 42 | import Data.Array as Array 43 | import Data.Foldable (class Foldable, foldl, intercalate) 44 | import Data.Maybe (Maybe(..), fromMaybe) 45 | import Data.Monoid (power) 46 | import Data.String as String 47 | import Data.String.CodeUnits as SCU 48 | import Data.Tuple (Tuple(..)) 49 | import Dodo (Doc) 50 | import Dodo as Dodo 51 | import Dodo.Internal (LocalOptions) 52 | import Tidy.Util (splitLines) 53 | 54 | data ForceBreak = ForceNone | ForceSpace | ForceBreak 55 | 56 | derive instance eqForceBreak :: Eq ForceBreak 57 | derive instance ordForceBreak :: Ord ForceBreak 58 | 59 | newtype LeadingComment a = LeadingComment 60 | { doc :: Doc a 61 | , left :: ForceBreak 62 | , lines :: Int 63 | , multiline :: Boolean 64 | , right :: ForceBreak 65 | } 66 | 67 | newtype TrailingComment a = TrailingComment 68 | { doc :: Doc a 69 | , left :: ForceBreak 70 | , multiline :: Boolean 71 | , right :: ForceBreak 72 | } 73 | 74 | instance Semigroup (LeadingComment a) where 75 | append (LeadingComment c1) (LeadingComment c2) 76 | | Dodo.isEmpty c1.doc = 77 | LeadingComment c2 78 | { left = max c1.left c2.left 79 | , lines = c1.lines + c2.lines 80 | } 81 | | Dodo.isEmpty c2.doc = 82 | LeadingComment c1 83 | { doc = c1.doc <> breaks ForceNone c2.lines 84 | , multiline = c1.multiline || c2.lines > 0 85 | , right = 86 | if c2.lines > 0 then 87 | ForceNone 88 | else 89 | max c1.right c2.right 90 | } 91 | | otherwise = do 92 | let br = max c1.right c2.left 93 | if c2.lines > 0 || br == ForceBreak then 94 | LeadingComment c1 95 | { doc = c1.doc <> breaks ForceBreak c2.lines <> c2.doc 96 | , multiline = true 97 | , right = c2.right 98 | } 99 | else 100 | LeadingComment c1 101 | { doc = c1.doc <> breakDoc br c2.doc 102 | , multiline = c1.multiline || c2.multiline 103 | , right = c2.right 104 | } 105 | 106 | instance Monoid (LeadingComment a) where 107 | mempty = LeadingComment 108 | { doc: mempty 109 | , left: ForceNone 110 | , lines: 0 111 | , multiline: false 112 | , right: ForceNone 113 | } 114 | 115 | instance Semigroup (TrailingComment a) where 116 | append (TrailingComment c1) (TrailingComment c2) 117 | | Dodo.isEmpty c1.doc = 118 | TrailingComment c2 { left = max c1.left c2.left } 119 | | Dodo.isEmpty c2.doc = 120 | TrailingComment c1 { right = max c1.right c2.right } 121 | | otherwise = 122 | TrailingComment c1 123 | { doc = c1.doc <> breakDoc (max c1.right c2.left) c2.doc 124 | , multiline = c1.multiline || c2.multiline 125 | , right = c2.right 126 | } 127 | 128 | instance Monoid (TrailingComment a) where 129 | mempty = TrailingComment 130 | { doc: mempty 131 | , left: ForceNone 132 | , multiline: false 133 | , right: ForceNone 134 | } 135 | 136 | newtype FormatDoc a = FormatDoc 137 | { doc :: Doc a 138 | , isEmpty :: Boolean 139 | , leading :: LeadingComment a 140 | , multiline :: Boolean 141 | , trailing :: TrailingComment a 142 | } 143 | 144 | type FormatDocOperator a = FormatDoc a -> FormatDoc a -> FormatDoc a 145 | 146 | instance semigroupFormatDoc :: Semigroup (FormatDoc a) where 147 | append = joinDoc (force identity) 148 | 149 | instance monoidFormatDoc :: Monoid (FormatDoc a) where 150 | mempty = FormatDoc 151 | { doc: mempty 152 | , leading: mempty 153 | , isEmpty: true 154 | , multiline: false 155 | , trailing: mempty 156 | } 157 | 158 | fromDoc :: forall a. Doc a -> FormatDoc a 159 | fromDoc doc 160 | | Dodo.isEmpty doc = mempty 161 | | otherwise = FormatDoc 162 | { doc 163 | , leading: mempty 164 | , isEmpty: false 165 | , multiline: false 166 | , trailing: mempty 167 | } 168 | 169 | text :: forall a. String -> FormatDoc a 170 | text = fromDoc <<< Dodo.text 171 | 172 | leadingLineComment :: forall a. String -> FormatDoc a -> FormatDoc a 173 | leadingLineComment str (FormatDoc doc) = FormatDoc doc 174 | { leading = comm <> doc.leading 175 | , isEmpty = false 176 | } 177 | where 178 | comm = LeadingComment 179 | { doc: Dodo.text str 180 | , left: ForceBreak 181 | , lines: 0 182 | , multiline: false 183 | , right: ForceBreak 184 | } 185 | 186 | trailingLineComment :: forall a. String -> FormatDoc a -> FormatDoc a 187 | trailingLineComment str (FormatDoc doc) = FormatDoc doc 188 | { trailing = comm <> doc.trailing 189 | , isEmpty = false 190 | } 191 | where 192 | comm = TrailingComment 193 | { doc: Dodo.text str 194 | , left: ForceSpace 195 | , multiline: false 196 | , right: ForceBreak 197 | } 198 | 199 | formatBlockComment :: forall a. String -> Tuple Boolean (Doc a) 200 | formatBlockComment = splitLines >>> Array.uncons >>> case _ of 201 | Nothing -> 202 | Tuple false mempty 203 | Just { head, tail } -> 204 | case prefixSpaces of 205 | Nothing -> 206 | Tuple false (Dodo.text head) 207 | Just ind -> 208 | Tuple true $ Dodo.withPosition \pos -> do 209 | let newIndent = if ind < pos.indent then 0 else ind 210 | let spaces = power " " newIndent 211 | let tailDocs = map (\str -> Dodo.text $ fromMaybe str $ String.stripPrefix (String.Pattern spaces) str) tail 212 | Dodo.lines 213 | [ Dodo.text head 214 | , Dodo.locally 215 | ( \prev -> 216 | if newIndent < prev.indent then 217 | prev 218 | { indentSpaces = spaces 219 | , indent = newIndent 220 | } 221 | else prev 222 | ) 223 | (intercalate Dodo.break tailDocs) 224 | ] 225 | where 226 | prefixSpaces = 227 | tail 228 | # Array.mapMaybe 229 | ( \str -> do 230 | let spaces = SCU.length $ String.takeWhile (eq (String.codePointFromChar ' ')) str 231 | guard (spaces < SCU.length str) $> spaces 232 | ) 233 | # Array.sort 234 | # Array.head 235 | 236 | leadingBlockComment :: forall a. String -> FormatDoc a -> FormatDoc a 237 | leadingBlockComment str (FormatDoc doc) = FormatDoc doc 238 | { leading = comm <> doc.leading 239 | , isEmpty = false 240 | } 241 | where 242 | Tuple multi commDoc = 243 | formatBlockComment str 244 | 245 | comm = LeadingComment 246 | { doc: commDoc 247 | , left: ForceSpace 248 | , lines: 0 249 | , multiline: multi 250 | , right: ForceSpace 251 | } 252 | 253 | trailingBlockComment :: forall a. String -> FormatDoc a -> FormatDoc a 254 | trailingBlockComment str (FormatDoc doc) = FormatDoc doc 255 | { trailing = comm <> doc.trailing 256 | , isEmpty = false 257 | } 258 | where 259 | Tuple multi commDoc = 260 | formatBlockComment str 261 | 262 | comm = TrailingComment 263 | { doc: commDoc 264 | , left: ForceSpace 265 | , multiline: multi 266 | , right: ForceSpace 267 | } 268 | 269 | anchor :: forall a. FormatDoc a -> FormatDoc a 270 | anchor (FormatDoc doc) = case doc.leading of 271 | LeadingComment comm | comm.lines > 0 -> 272 | FormatDoc doc 273 | { leading = LeadingComment comm { lines = 0 } 274 | , multiline = true 275 | } 276 | _ -> 277 | FormatDoc doc 278 | 279 | flatten :: forall a. FormatDoc a -> FormatDoc a 280 | flatten = flattenMax 0 281 | 282 | flattenMax :: forall a. Int -> FormatDoc a -> FormatDoc a 283 | flattenMax n (FormatDoc doc) = case doc.leading of 284 | LeadingComment comm -> 285 | FormatDoc doc 286 | { leading = LeadingComment comm { lines = min comm.lines n } 287 | } 288 | 289 | flexGroup :: forall a. FormatDoc a -> FormatDoc a 290 | flexGroup (FormatDoc doc) 291 | | doc.multiline = FormatDoc doc 292 | | otherwise = FormatDoc doc { doc = Dodo.flexGroup doc.doc } 293 | 294 | indent :: forall a. FormatDoc a -> FormatDoc a 295 | indent = mapDocs Dodo.indent 296 | 297 | align :: forall a. Int -> FormatDoc a -> FormatDoc a 298 | align = mapDocs <<< Dodo.align 299 | 300 | alignCurrentColumn :: forall a. FormatDoc a -> FormatDoc a 301 | alignCurrentColumn = mapDocs Dodo.alignCurrentColumn 302 | 303 | locally :: forall a. (LocalOptions -> LocalOptions) -> FormatDoc a -> FormatDoc a 304 | locally k (FormatDoc doc) = FormatDoc doc { doc = Dodo.locally k doc.doc } 305 | 306 | sourceBreak :: forall a. Int -> FormatDoc a -> FormatDoc a 307 | sourceBreak n (FormatDoc doc) = do 308 | let LeadingComment comm = doc.leading 309 | FormatDoc doc 310 | { isEmpty = false 311 | , leading = LeadingComment comm { lines = comm.lines + n } 312 | } 313 | 314 | forceMinSourceBreaks :: forall a. Int -> FormatDoc a -> FormatDoc a 315 | forceMinSourceBreaks n (FormatDoc doc) 316 | | doc.isEmpty = FormatDoc doc 317 | | otherwise = do 318 | let LeadingComment comm = doc.leading 319 | FormatDoc doc 320 | { leading = LeadingComment comm { lines = max comm.lines n } 321 | } 322 | 323 | space :: forall a. FormatDocOperator a 324 | space = joinDoc (force (append Dodo.space)) 325 | 326 | break :: forall a. FormatDocOperator a 327 | break = joinDoc (force (append Dodo.break)) 328 | 329 | spaceBreak :: forall a. FormatDocOperator a 330 | spaceBreak = joinDoc (force (append Dodo.spaceBreak)) 331 | 332 | flexSpaceBreak :: forall a. FormatDocOperator a 333 | flexSpaceBreak = joinDoc \f m doc -> case f of 334 | ForceBreak -> 335 | Tuple true (Dodo.break <> doc) 336 | _ -> 337 | if m then 338 | Tuple true (Dodo.spaceBreak <> doc) 339 | else 340 | Tuple false (Dodo.flexGroup (Dodo.spaceBreak <> doc)) 341 | 342 | flexSoftSpace :: forall a. FormatDocOperator a 343 | flexSoftSpace = joinDoc \f m doc -> case f of 344 | ForceBreak -> 345 | Tuple true (Dodo.break <> doc) 346 | ForceSpace -> 347 | if m then 348 | Tuple true (Dodo.space <> doc) 349 | else 350 | Tuple false (Dodo.flexGroup (Dodo.space <> doc)) 351 | ForceNone -> 352 | if m then 353 | Tuple true (softSpaceDoc <> doc) 354 | else 355 | Tuple false (Dodo.flexGroup (softSpaceDoc <> doc)) 356 | 357 | flexSoftBreak :: forall a. FormatDocOperator a 358 | flexSoftBreak = joinDoc \f m doc -> case f of 359 | ForceBreak -> 360 | Tuple true (Dodo.break <> doc) 361 | ForceSpace -> 362 | if m then 363 | Tuple true (Dodo.space <> doc) 364 | else 365 | Tuple false (Dodo.flexGroup (Dodo.spaceBreak <> doc)) 366 | ForceNone -> 367 | if m then 368 | Tuple true (Dodo.break <> doc) 369 | else 370 | Tuple false (Dodo.flexGroup (Dodo.softBreak <> doc)) 371 | 372 | softBreak :: forall a. FormatDocOperator a 373 | softBreak = joinDoc \f m doc -> case f of 374 | ForceBreak -> 375 | Tuple true (Dodo.break <> doc) 376 | ForceSpace -> 377 | Tuple m (Dodo.spaceBreak <> doc) 378 | ForceNone -> 379 | Tuple m (Dodo.softBreak <> doc) 380 | 381 | softSpace :: forall a. FormatDocOperator a 382 | softSpace = joinDoc \f m doc -> case f of 383 | ForceBreak -> 384 | Tuple true (Dodo.break <> doc) 385 | ForceSpace -> 386 | Tuple m (Dodo.space <> doc) 387 | ForceNone -> 388 | Tuple m (softSpaceDoc <> doc) 389 | 390 | -- | Warning: This is not an associative join operation, and *requires* 391 | -- | right associativity. You will always get double breaks when used 392 | -- | with left associativity. 393 | flexDoubleBreak :: forall a. FormatDocOperator a 394 | flexDoubleBreak (FormatDoc doc1) (FormatDoc doc2) 395 | | doc1.isEmpty = FormatDoc doc2 396 | | doc2.isEmpty = FormatDoc doc1 397 | | otherwise = do 398 | let TrailingComment comm1 = doc1.trailing 399 | let LeadingComment comm2 = doc2.leading 400 | let docLeft = doc1.doc <> breakDoc comm1.left comm1.doc 401 | let docRight = comm2.doc <> breakDoc comm2.right doc2.doc 402 | if comm2.lines >= 2 || doc1.multiline then 403 | FormatDoc doc1 404 | { doc = docLeft <> Dodo.break <> Dodo.break <> docRight 405 | , multiline = true 406 | , trailing = doc2.trailing 407 | } 408 | else 409 | FormatDoc doc1 410 | { doc = Dodo.flexSelect docLeft mempty Dodo.break <> Dodo.break <> docRight 411 | , multiline = true 412 | , trailing = doc2.trailing 413 | } 414 | 415 | isEmpty :: forall a. FormatDoc a -> Boolean 416 | isEmpty (FormatDoc doc) = doc.isEmpty 417 | 418 | breakDoc :: forall a. ForceBreak -> Doc a -> Doc a 419 | breakDoc br doc 420 | | Dodo.isEmpty doc = doc 421 | | otherwise = case br of 422 | ForceBreak -> Dodo.break <> doc 423 | ForceSpace -> Dodo.space <> doc 424 | ForceNone -> doc 425 | 426 | breaks :: forall a. ForceBreak -> Int -> Doc a 427 | breaks fl n 428 | | n >= 2 = Dodo.break <> Dodo.break 429 | | n == 1 = Dodo.break 430 | | otherwise = case fl of 431 | ForceBreak -> Dodo.break 432 | ForceSpace -> Dodo.space 433 | ForceNone -> mempty 434 | 435 | joinDoc :: forall a. (ForceBreak -> Boolean -> Doc a -> Tuple Boolean (Doc a)) -> FormatDocOperator a 436 | joinDoc spaceFn (FormatDoc doc1) (FormatDoc doc2) 437 | | doc1.isEmpty = FormatDoc doc2 438 | | doc2.isEmpty = FormatDoc doc1 439 | | otherwise = do 440 | let TrailingComment comm1 = doc1.trailing 441 | let LeadingComment comm2 = doc2.leading 442 | let docLeft = doc1.doc <> breakDoc comm1.left comm1.doc 443 | let docRight = comm2.doc <> breakDoc comm2.right doc2.doc 444 | if comm2.lines > 0 then 445 | FormatDoc doc1 446 | { doc = docLeft <> breaks ForceBreak comm2.lines <> docRight 447 | , multiline = true 448 | , trailing = doc2.trailing 449 | } 450 | else do 451 | let Tuple m3 doc3 = spaceFn (max comm1.right comm2.left) (comm2.multiline || doc2.multiline) docRight 452 | FormatDoc doc1 453 | { doc = docLeft <> doc3 454 | , multiline = comm1.multiline || doc1.multiline || m3 455 | , trailing = doc2.trailing 456 | } 457 | 458 | force :: forall a. (Doc a -> Doc a) -> (ForceBreak -> Boolean -> Doc a -> Tuple Boolean (Doc a)) 459 | force k f m doc = case f of 460 | ForceBreak -> 461 | Tuple true (Dodo.break <> doc) 462 | _ -> 463 | Tuple m (k doc) 464 | 465 | mapDoc :: forall a. (Doc a -> Doc a) -> FormatDoc a -> FormatDoc a 466 | mapDoc k (FormatDoc doc) 467 | | doc.isEmpty = FormatDoc doc 468 | | otherwise = FormatDoc doc { doc = k doc.doc } 469 | 470 | mapDocs :: forall a. (Doc a -> Doc a) -> FormatDoc a -> FormatDoc a 471 | mapDocs k (FormatDoc doc) 472 | | doc.isEmpty = FormatDoc doc 473 | | otherwise = do 474 | let LeadingComment comm1 = doc.leading 475 | let TrailingComment comm2 = doc.trailing 476 | FormatDoc doc 477 | { doc = k doc.doc 478 | , leading = LeadingComment comm1 { doc = k comm1.doc } 479 | , trailing = TrailingComment comm2 { doc = k comm2.doc } 480 | } 481 | 482 | toDoc :: forall a. FormatDoc a -> Doc a 483 | toDoc (FormatDoc doc) 484 | | doc.isEmpty = mempty 485 | | otherwise = do 486 | let LeadingComment comm1 = doc.leading 487 | let TrailingComment comm2 = doc.trailing 488 | comm1.doc 489 | <> breakDoc comm1.right doc.doc 490 | <> breakDoc comm2.left comm2.doc 491 | 492 | joinWithMap 493 | :: forall f a b 494 | . Foldable f 495 | => (FormatDoc a -> FormatDoc a -> FormatDoc a) 496 | -> (b -> FormatDoc a) 497 | -> f b 498 | -> FormatDoc a 499 | joinWithMap op k = foldl go mempty 500 | where 501 | go a b 502 | | isEmpty a = k b 503 | | otherwise = op a (k b) 504 | 505 | joinWith 506 | :: forall f a 507 | . Foldable f 508 | => (FormatDoc a -> FormatDoc a -> FormatDoc a) 509 | -> f (FormatDoc a) 510 | -> FormatDoc a 511 | joinWith = flip joinWithMap identity 512 | 513 | softSpaceDoc :: forall a. Doc a 514 | softSpaceDoc = Dodo.flexAlt mempty Dodo.space 515 | -------------------------------------------------------------------------------- /src/Tidy/Hang.purs: -------------------------------------------------------------------------------- 1 | module Tidy.Hang 2 | ( HangingDoc 3 | , HangingOp(..) 4 | , hang 5 | , hangWithIndent 6 | , hangBreak 7 | , hangApp 8 | , hangOps 9 | , hangHead 10 | , overHangHead 11 | , hangConcatApp 12 | , toFormatDoc 13 | ) where 14 | 15 | import Prelude 16 | 17 | import Data.Array as Array 18 | import Data.Array.NonEmpty (NonEmptyArray) 19 | import Data.Array.NonEmpty as NonEmptyArray 20 | import Data.Maybe (maybe) 21 | import Data.Tuple (Tuple(..), fst, snd) 22 | import Dodo (Doc) 23 | import Dodo as Dodo 24 | import Tidy.Doc (ForceBreak(..), FormatDoc(..), LeadingComment(..), TrailingComment(..), align, break, breakDoc, flatten, flexGroup, forceMinSourceBreaks, indent) 25 | 26 | data HangingDoc a 27 | = HangBreak (FormatDoc a) 28 | | HangOps (FormatDoc a -> FormatDoc a) (HangingDoc a) (NonEmptyArray (HangingOp a)) 29 | | HangApp (FormatDoc a -> FormatDoc a) (HangingDoc a) (NonEmptyArray (HangingDoc a)) 30 | 31 | data HangingOp a = HangingOp Int (FormatDoc a) (HangingDoc a) 32 | 33 | hang :: forall a. FormatDoc a -> HangingDoc a -> HangingDoc a 34 | hang a = HangApp indent (hangBreak a) <<< pure 35 | 36 | hangWithIndent :: forall a. (FormatDoc a -> FormatDoc a) -> HangingDoc a -> Array (HangingDoc a) -> HangingDoc a 37 | hangWithIndent ind a = maybe a (HangApp ind a) <<< NonEmptyArray.fromArray 38 | 39 | hangBreak :: forall a. FormatDoc a -> HangingDoc a 40 | hangBreak = HangBreak <<< flexGroup 41 | 42 | hangApp :: forall a. HangingDoc a -> NonEmptyArray (HangingDoc a) -> HangingDoc a 43 | hangApp = HangApp indent 44 | 45 | hangOps :: forall a. HangingDoc a -> NonEmptyArray (HangingOp a) -> HangingDoc a 46 | hangOps = HangOps indent 47 | 48 | hangConcatApp :: forall a. HangingDoc a -> NonEmptyArray (HangingDoc a) -> HangingDoc a 49 | hangConcatApp a b = case a of 50 | HangApp ind head tail -> HangApp ind head (tail <> b) 51 | _ -> HangApp indent a b 52 | 53 | hangHead :: forall a. HangingDoc a -> FormatDoc a 54 | hangHead = case _ of 55 | HangBreak doc -> doc 56 | HangOps _ doc _ -> hangHead doc 57 | HangApp _ doc _ -> hangHead doc 58 | 59 | overHangHead :: forall a. (FormatDoc a -> FormatDoc a) -> HangingDoc a -> HangingDoc a 60 | overHangHead f = go 61 | where 62 | go = case _ of 63 | HangBreak doc -> HangBreak (f doc) 64 | HangOps ind doc docs -> HangOps ind (go doc) docs 65 | HangApp ind doc docs -> HangApp ind (go doc) docs 66 | 67 | forceBreaks :: forall a. Int -> Doc a 68 | forceBreaks n 69 | | n >= 2 = Dodo.break <> Dodo.break 70 | | otherwise = Dodo.break 71 | 72 | breaks :: forall a. ForceBreak -> Int -> Doc a 73 | breaks fl n 74 | | fl == ForceBreak || n > 0 = forceBreaks n 75 | | fl == ForceSpace = Dodo.space 76 | | otherwise = mempty 77 | 78 | toFormatDoc :: forall a. HangingDoc a -> FormatDoc a 79 | toFormatDoc = fst <<< goInit 80 | where 81 | goInit = case _ of 82 | HangBreak doc -> 83 | Tuple doc doc 84 | HangApp ind head tail -> do 85 | let 86 | { init, last } = NonEmptyArray.unsnoc tail 87 | next = Array.foldr goInitApp (goLastApp last) init 88 | this = fst (goInit head) 89 | docGroup = flexSelect this (ind (fst next)) (indMulti head ind (fst next)) 90 | docBreak = this `break` ind (snd next) 91 | Tuple docGroup docBreak 92 | HangOps ind head tail -> do 93 | let 94 | { init, last } = NonEmptyArray.unsnoc tail 95 | next = Array.foldr (goInitOp ind) (goLastOp ind last) init 96 | this = fst (goInit head) 97 | docGroup = flexGroup this <> ind (fst next) 98 | docBreak = this `break` ind (snd next) 99 | Tuple docGroup docBreak 100 | 101 | goLast = case _ of 102 | HangBreak doc -> do 103 | let doc' = docJoin doc 104 | Tuple doc' doc' 105 | HangApp ind head tail -> do 106 | let 107 | { init, last } = NonEmptyArray.unsnoc tail 108 | next = Array.foldr goInitApp (goLastApp last) init 109 | this = fst (goInit head) 110 | docGroup = flexSelectJoin this (fst next) (indMulti head ind (fst next)) 111 | docBreak = docJoin this <> indMulti head ind (fst next) 112 | Tuple docGroup docBreak 113 | HangOps ind head tail -> do 114 | let 115 | { init, last } = NonEmptyArray.unsnoc tail 116 | next = Array.foldr (goInitOp ind) (goLastOp ind last) init 117 | this = fst (goInit head) 118 | docGroup = flexSelectJoin this (fst next) (ind (fst next)) 119 | docBreak = docJoin this `break` ind (snd next) 120 | Tuple docGroup docBreak 121 | 122 | goInitApp doc next = do 123 | let 124 | this = fst (goInit doc) 125 | docGroup = flexSelectJoin this (fst next) (snd next) 126 | docBreak = docJoin this <> snd next 127 | Tuple docGroup docBreak 128 | 129 | goLastApp doc = do 130 | let 131 | this = goLast doc 132 | docGroup = flexGroup (fst this) 133 | docBreak = snd this 134 | Tuple docGroup docBreak 135 | 136 | goInitOp ind (HangingOp width op doc) next = do 137 | let 138 | Tuple op' doc' = realignOp op doc 139 | algn = if width <= 1 then align 2 else identity 140 | docOprd = fst (goInitOperand algn ind doc') 141 | docGroup = flexSelectJoin (op' <> docOprd) (fst next) (snd next) 142 | docBreak = docJoin op' <> docOprd <> snd next 143 | Tuple docGroup docBreak 144 | 145 | goLastOp ind (HangingOp width op doc) = do 146 | let 147 | algn = if width <= 1 then align 2 else identity 148 | next = goLastOperand algn ind doc 149 | docIndent = snd next 150 | docGroup = flexSelectJoin op (fst next) docIndent 151 | docBreak = docJoin op <> docIndent 152 | Tuple docGroup docBreak 153 | 154 | goInitOperand prevAlgn prevInd = case _ of 155 | HangBreak doc -> do 156 | let doc' = prevInd (flexGroup (docJoin doc)) 157 | Tuple doc' doc' 158 | HangApp ind head tail -> do 159 | let 160 | { init, last } = NonEmptyArray.unsnoc tail 161 | next = Array.foldr goInitApp (goLastApp last) init 162 | this = fst (goInit head) 163 | docGroup = 164 | flexSelectJoin (prevInd this) 165 | (indMulti head (prevAlgn <<< ind) (fst next)) 166 | (prevInd (indMulti head ind (fst next))) 167 | docBreak = prevInd (docJoin this <> ind (snd next)) 168 | Tuple docGroup docBreak 169 | HangOps ind head tail -> do 170 | let 171 | { init, last } = NonEmptyArray.unsnoc tail 172 | next = Array.foldr (goInitOp ind) (goLastOp ind last) init 173 | this = fst (goInit head) 174 | docGroup = 175 | flexSelectJoin (prevInd this) 176 | (prevAlgn (ind (fst next))) 177 | (prevInd (ind (fst next))) 178 | docBreak = prevInd (docJoin this <> ind (snd next)) 179 | Tuple docGroup docBreak 180 | 181 | goLastOperand prevAlgn prevInd = case _ of 182 | HangBreak doc -> do 183 | let doc' = flexGroup (docJoin doc) 184 | Tuple doc' (prevInd doc') 185 | HangApp ind head tail -> do 186 | let 187 | { init, last } = NonEmptyArray.unsnoc tail 188 | next = Array.foldr goInitApp (goLastApp last) init 189 | this = fst $ goInit $ case head of 190 | HangApp _ _ _ -> overHangHead (forceMinSourceBreaks 1) head 191 | _ -> head 192 | docIndent = indMulti head ind (fst next) 193 | docGroup = flexSelectJoin this (fst next) docIndent 194 | docBreak = flexSelectJoin (prevInd this) (prevAlgn docIndent) (prevInd docIndent) 195 | Tuple docGroup docBreak 196 | HangOps ind head tail -> do 197 | let 198 | { init, last } = NonEmptyArray.unsnoc tail 199 | next = Array.foldr (goInitOp ind) (goLastOp ind last) init 200 | this = fst (goInit head) 201 | docIndent = ind (fst next) 202 | docGroup = flexSelectJoin this (fst next) docIndent 203 | docBreak = flexSelectJoin (prevInd this) (prevAlgn docIndent) (prevInd docIndent) 204 | Tuple docGroup docBreak 205 | 206 | flexSelect (FormatDoc doc1) (FormatDoc doc2) (FormatDoc doc3) = do 207 | let 208 | TrailingComment comm1r = doc1.trailing 209 | 210 | doc1' = 211 | doc1.doc 212 | <> breakDoc comm1r.left comm1r.doc 213 | 214 | LeadingComment comm2l = doc2.leading 215 | TrailingComment comm2r = doc2.trailing 216 | 217 | doc2' = 218 | breaks (max comm1r.right comm2l.left) comm2l.lines 219 | <> comm2l.doc 220 | <> breakDoc comm2l.right doc2.doc 221 | <> breakDoc comm2r.left comm2r.doc 222 | 223 | LeadingComment comm3l = doc3.leading 224 | TrailingComment comm3r = doc3.trailing 225 | 226 | doc3' = 227 | breaks (max comm1r.right comm3l.left) comm3l.lines 228 | <> comm3l.doc 229 | <> breakDoc comm2l.right doc3.doc 230 | <> breakDoc comm3r.left comm3r.doc 231 | 232 | m1 = doc1.multiline || comm1r.multiline 233 | m2 = comm2l.multiline || doc2.multiline || comm2r.multiline 234 | m3 = comm3l.multiline || doc3.multiline || comm3r.multiline 235 | 236 | FormatDoc 237 | { doc: Dodo.flexSelect doc1' doc2' doc3' 238 | , leading: doc1.leading 239 | , isEmpty: false 240 | , multiline: m1 || (m2 && m3) 241 | , trailing: 242 | TrailingComment 243 | { doc: mempty 244 | , left: ForceNone 245 | , multiline: false 246 | , right: max comm2r.right comm3r.right 247 | } 248 | } 249 | 250 | flexSelectJoin (FormatDoc doc1) (FormatDoc doc2) (FormatDoc doc3) = do 251 | let 252 | LeadingComment comm1l = doc1.leading 253 | TrailingComment comm1r = doc1.trailing 254 | 255 | break 256 | | comm1l.left == ForceBreak || comm1l.lines > 0 = Dodo.break 257 | | otherwise = Dodo.spaceBreak 258 | 259 | doc1' = 260 | break 261 | <> comm1l.doc 262 | <> breakDoc comm1l.right doc1.doc 263 | <> breakDoc comm1r.left comm1r.doc 264 | 265 | LeadingComment comm2l = doc2.leading 266 | TrailingComment comm2r = doc2.trailing 267 | 268 | doc2' = 269 | breaks (max comm1r.right comm2l.left) comm2l.lines 270 | <> comm2l.doc 271 | <> breakDoc comm2l.right doc2.doc 272 | <> breakDoc comm2r.left comm2r.doc 273 | 274 | LeadingComment comm3l = doc3.leading 275 | TrailingComment comm3r = doc3.trailing 276 | 277 | doc3' = 278 | breaks (max comm1r.right comm3l.left) comm3l.lines 279 | <> comm3l.doc 280 | <> breakDoc comm2l.right doc3.doc 281 | <> breakDoc comm3r.left comm3r.doc 282 | 283 | m1 = comm1l.multiline || doc1.multiline || comm1r.multiline 284 | m2 = comm2l.multiline || doc2.multiline || comm2r.multiline 285 | m3 = comm3l.multiline || doc3.multiline || comm3r.multiline 286 | 287 | FormatDoc 288 | { doc: Dodo.flexSelect doc1' doc2' doc3' 289 | , leading: mempty 290 | , isEmpty: false 291 | , multiline: m1 || (m2 && m3) 292 | , trailing: 293 | TrailingComment 294 | { doc: mempty 295 | , left: ForceNone 296 | , multiline: false 297 | , right: max comm2r.right comm3r.right 298 | } 299 | } 300 | 301 | docJoin fdoc@(FormatDoc doc) 302 | | doc.isEmpty = fdoc 303 | | otherwise = do 304 | let LeadingComment comm = doc.leading 305 | if comm.left == ForceBreak || comm.lines > 0 then 306 | fdoc 307 | else if comm.multiline || doc.multiline then 308 | forceMinSourceBreaks 1 fdoc 309 | else 310 | FormatDoc doc 311 | { doc = Dodo.spaceBreak <> comm.doc <> breakDoc comm.right doc.doc 312 | , leading = mempty 313 | } 314 | 315 | realignOp op doc = case op, hangHead doc of 316 | FormatDoc doc1@{ leading: LeadingComment comm1, trailing: TrailingComment comm2 }, 317 | FormatDoc { leading: LeadingComment comm3 } 318 | | comm1.left /= ForceBreak && comm1.lines == 0 && comm2.right /= ForceBreak && comm3.left /= ForceBreak && comm3.lines > 0 -> 319 | realignOp (forceMinSourceBreaks 1 op) (overHangHead flatten doc) 320 | | HangBreak _ <- doc 321 | , comm2.right /= ForceBreak && comm3.left /= ForceBreak && comm3.lines == 0 && (comm3.multiline || doc1.multiline) -> 322 | Tuple op (overHangHead (forceMinSourceBreaks 1) doc) 323 | _, _ -> 324 | Tuple op doc 325 | 326 | indMulti head ind doc = case head of 327 | HangApp _ _ _ -> doc 328 | _ -> ind doc 329 | -------------------------------------------------------------------------------- /src/Tidy/Operators.purs: -------------------------------------------------------------------------------- 1 | module Tidy.Operators where 2 | 3 | import Prelude 4 | 5 | import Data.Array as Array 6 | import Data.Either (Either(..)) 7 | import Data.Foldable (foldl, foldr) 8 | import Data.Map as Map 9 | import Data.Maybe (Maybe(..), fromMaybe) 10 | import Data.Tuple (Tuple(..), snd, uncurry) 11 | import PureScript.CST.Errors (ParseError) 12 | import PureScript.CST.Lexer as Lexer 13 | import PureScript.CST.TokenStream (TokenStep(..), TokenStream) 14 | import PureScript.CST.TokenStream as TokenStream 15 | import PureScript.CST.Types (Declaration(..), Export(..), FixityOp(..), IntValue(..), Module(..), ModuleBody(..), ModuleHeader(..), Name(..), Operator(..), Separated(..), Token(..), Wrapped(..)) 16 | import Tidy.Precedence (OperatorNamespace(..), Precedence, PrecedenceMap, QualifiedOperator(..), insertOperator, lookupOperator, remapOperators) 17 | 18 | parseOperatorTable :: Array String -> PrecedenceMap 19 | parseOperatorTable = foldr (uncurry insertOperator) Map.empty <<< Array.mapMaybe parseOperatorPrec 20 | 21 | parseOperatorPrec :: String -> Maybe (Tuple QualifiedOperator Precedence) 22 | parseOperatorPrec = Lexer.lex >>> tokenStreamToArray >>> case _ of 23 | Right [ TokSymbolName modName op, TokInt _ (SmallInt prec) ] -> 24 | Just $ Tuple (QualifiedOperator modName OperatorValue (Operator op)) prec 25 | Right [ TokSymbolName modName op, TokLowerName Nothing "type", TokInt _ (SmallInt prec) ] -> 26 | Just $ Tuple (QualifiedOperator modName OperatorType (Operator op)) prec 27 | _ -> 28 | Nothing 29 | where 30 | tokenStreamToArray :: TokenStream -> Either ParseError (Array Token) 31 | tokenStreamToArray = go [] 32 | where 33 | go acc = TokenStream.step >>> case _ of 34 | TokenEOF _ _ -> 35 | Right acc 36 | TokenError _ err _ _ -> 37 | Left err 38 | TokenCons tok _ next _ -> 39 | go (Array.snoc acc tok.value) next 40 | 41 | resolveOperatorExports :: forall e. PrecedenceMap -> Module e -> PrecedenceMap 42 | resolveOperatorExports 43 | precMap 44 | mod@ 45 | ( Module 46 | { header: ModuleHeader { exports, name: Name { name: modName } } 47 | , body: ModuleBody { decls } 48 | } 49 | ) = 50 | case exports of 51 | Nothing -> 52 | foldl goDecl precMap decls 53 | Just (Wrapped { value: Separated { head, tail } }) -> 54 | foldl goExport precMap $ Array.cons head $ map snd tail 55 | 56 | where 57 | remappedPrecMap = 58 | remapOperators precMap mod 59 | 60 | goExport pm = fromMaybe pm <<< case _ of 61 | ExportOp (Name { name: op }) -> do 62 | prec <- lookupOperator (QualifiedOperator Nothing OperatorValue op) remappedPrecMap 63 | pure $ insertOperator (QualifiedOperator (Just modName) OperatorValue op) prec pm 64 | ExportTypeOp _ (Name { name: op }) -> do 65 | prec <- lookupOperator (QualifiedOperator Nothing OperatorType op) remappedPrecMap 66 | pure $ insertOperator (QualifiedOperator (Just modName) OperatorType op) prec pm 67 | ExportModule _ (Name { name: exportModName }) -> do 68 | prec <- Map.lookup (Just exportModName) remappedPrecMap 69 | pure $ Map.insertWith Map.union (Just modName) prec pm 70 | _ -> 71 | Nothing 72 | 73 | goDecl pm = case _ of 74 | DeclFixity { prec: Tuple _ prec, operator } -> 75 | case operator of 76 | FixityValue _ _ (Name { name: op }) -> 77 | insertOperator (QualifiedOperator (Just modName) OperatorValue op) prec pm 78 | FixityType _ _ _ (Name { name: op }) -> 79 | insertOperator (QualifiedOperator (Just modName) OperatorType op) prec pm 80 | _ -> 81 | pm 82 | -------------------------------------------------------------------------------- /src/Tidy/Operators/Defaults.purs: -------------------------------------------------------------------------------- 1 | -------------------------------------------- 2 | -- This module is generated. DO NOT EDIT! -- 3 | -------------------------------------------- 4 | module Tidy.Operators.Defaults where 5 | 6 | defaultOperators :: Array String 7 | defaultOperators = 8 | [ """Control.Alt.($>) 4""" 9 | , """Control.Alt.(<#>) 1""" 10 | , """Control.Alt.(<$) 4""" 11 | , """Control.Alt.(<$>) 4""" 12 | , """Control.Alt.(<@>) 4""" 13 | , """Control.Alt.(<|>) 3""" 14 | , """Control.Alternative.($>) 4""" 15 | , """Control.Alternative.(*>) 4""" 16 | , """Control.Alternative.(<#>) 1""" 17 | , """Control.Alternative.(<$) 4""" 18 | , """Control.Alternative.(<$>) 4""" 19 | , """Control.Alternative.(<*) 4""" 20 | , """Control.Alternative.(<*>) 4""" 21 | , """Control.Alternative.(<@>) 4""" 22 | , """Control.Alternative.(<|>) 3""" 23 | , """Control.Applicative.($>) 4""" 24 | , """Control.Applicative.(*>) 4""" 25 | , """Control.Applicative.(<#>) 1""" 26 | , """Control.Applicative.(<$) 4""" 27 | , """Control.Applicative.(<$>) 4""" 28 | , """Control.Applicative.(<*) 4""" 29 | , """Control.Applicative.(<*>) 4""" 30 | , """Control.Applicative.(<@>) 4""" 31 | , """Control.Apply.($>) 4""" 32 | , """Control.Apply.(*>) 4""" 33 | , """Control.Apply.(<#>) 1""" 34 | , """Control.Apply.(<$) 4""" 35 | , """Control.Apply.(<$>) 4""" 36 | , """Control.Apply.(<*) 4""" 37 | , """Control.Apply.(<*>) 4""" 38 | , """Control.Apply.(<@>) 4""" 39 | , """Control.Biapply.(*>>) 4""" 40 | , """Control.Biapply.(<<$>>) 4""" 41 | , """Control.Biapply.(<<*) 4""" 42 | , """Control.Biapply.(<<*>>) 4""" 43 | , """Control.Bind.($>) 4""" 44 | , """Control.Bind.(*>) 4""" 45 | , """Control.Bind.(<#>) 1""" 46 | , """Control.Bind.(<$) 4""" 47 | , """Control.Bind.(<$>) 4""" 48 | , """Control.Bind.(<*) 4""" 49 | , """Control.Bind.(<*>) 4""" 50 | , """Control.Bind.(<=<) 1""" 51 | , """Control.Bind.(<@>) 4""" 52 | , """Control.Bind.(=<<) 1""" 53 | , """Control.Bind.(>=>) 1""" 54 | , """Control.Bind.(>>=) 1""" 55 | , """Control.Category.(<<<) 9""" 56 | , """Control.Category.(>>>) 9""" 57 | , """Control.Comonad.($>) 4""" 58 | , """Control.Comonad.(<#>) 1""" 59 | , """Control.Comonad.(<$) 4""" 60 | , """Control.Comonad.(<$>) 4""" 61 | , """Control.Comonad.(<<=) 1""" 62 | , """Control.Comonad.(<@>) 4""" 63 | , """Control.Comonad.(=<=) 1""" 64 | , """Control.Comonad.(=>=) 1""" 65 | , """Control.Comonad.(=>>) 1""" 66 | , """Control.Comonad.Cofree.(:<) 5""" 67 | , """Control.Coroutine.($$) 2""" 68 | , """Control.Coroutine.($~) 2""" 69 | , """Control.Coroutine.(/\) 3""" 70 | , """Control.Coroutine.(\/) 3""" 71 | , """Control.Coroutine.(~$) 2""" 72 | , """Control.Coroutine.(~~) 2""" 73 | , """Control.Extend.($>) 4""" 74 | , """Control.Extend.(<#>) 1""" 75 | , """Control.Extend.(<$) 4""" 76 | , """Control.Extend.(<$>) 4""" 77 | , """Control.Extend.(<<=) 1""" 78 | , """Control.Extend.(<@>) 4""" 79 | , """Control.Extend.(=<=) 1""" 80 | , """Control.Extend.(=>=) 1""" 81 | , """Control.Extend.(=>>) 1""" 82 | , """Control.Monad.($>) 4""" 83 | , """Control.Monad.(*>) 4""" 84 | , """Control.Monad.(<#>) 1""" 85 | , """Control.Monad.(<$) 4""" 86 | , """Control.Monad.(<$>) 4""" 87 | , """Control.Monad.(<*) 4""" 88 | , """Control.Monad.(<*>) 4""" 89 | , """Control.Monad.(<=<) 1""" 90 | , """Control.Monad.(<@>) 4""" 91 | , """Control.Monad.(=<<) 1""" 92 | , """Control.Monad.(>=>) 1""" 93 | , """Control.Monad.(>>=) 1""" 94 | , """Control.MonadPlus.($>) 4""" 95 | , """Control.MonadPlus.(*>) 4""" 96 | , """Control.MonadPlus.(<#>) 1""" 97 | , """Control.MonadPlus.(<$) 4""" 98 | , """Control.MonadPlus.(<$>) 4""" 99 | , """Control.MonadPlus.(<*) 4""" 100 | , """Control.MonadPlus.(<*>) 4""" 101 | , """Control.MonadPlus.(<=<) 1""" 102 | , """Control.MonadPlus.(<@>) 4""" 103 | , """Control.MonadPlus.(<|>) 3""" 104 | , """Control.MonadPlus.(=<<) 1""" 105 | , """Control.MonadPlus.(>=>) 1""" 106 | , """Control.MonadPlus.(>>=) 1""" 107 | , """Control.Plus.($>) 4""" 108 | , """Control.Plus.(<#>) 1""" 109 | , """Control.Plus.(<$) 4""" 110 | , """Control.Plus.(<$>) 4""" 111 | , """Control.Plus.(<@>) 4""" 112 | , """Control.Plus.(<|>) 3""" 113 | , """Control.Semigroupoid.(<<<) 9""" 114 | , """Control.Semigroupoid.(>>>) 9""" 115 | , """Data.Argonaut.(.!=) 6""" 116 | , """Data.Argonaut.(.:) 7""" 117 | , """Data.Argonaut.(.:!) 7""" 118 | , """Data.Argonaut.(.:?) 7""" 119 | , """Data.Argonaut.(:=) 7""" 120 | , """Data.Argonaut.(:=?) 7""" 121 | , """Data.Argonaut.(~>) 6""" 122 | , """Data.Argonaut.(~>?) 6""" 123 | , """Data.Argonaut.Decode.(.!=) 6""" 124 | , """Data.Argonaut.Decode.(.:) 7""" 125 | , """Data.Argonaut.Decode.(.:!) 7""" 126 | , """Data.Argonaut.Decode.(.:?) 7""" 127 | , """Data.Argonaut.Decode.Combinators.(.!=) 6""" 128 | , """Data.Argonaut.Decode.Combinators.(.:) 7""" 129 | , """Data.Argonaut.Decode.Combinators.(.:!) 7""" 130 | , """Data.Argonaut.Decode.Combinators.(.:?) 7""" 131 | , """Data.Argonaut.Encode.(:=) 7""" 132 | , """Data.Argonaut.Encode.(:=?) 7""" 133 | , """Data.Argonaut.Encode.(~>) 6""" 134 | , """Data.Argonaut.Encode.(~>?) 6""" 135 | , """Data.Argonaut.Encode.Combinators.(:=) 7""" 136 | , """Data.Argonaut.Encode.Combinators.(:=?) 7""" 137 | , """Data.Argonaut.Encode.Combinators.(~>) 6""" 138 | , """Data.Argonaut.Encode.Combinators.(~>?) 6""" 139 | , """Data.Array.(!!) 8""" 140 | , """Data.Array.(..) 8""" 141 | , """Data.Array.(:) 6""" 142 | , """Data.Array.(\\) 5""" 143 | , """Data.Array.NonEmpty.(!!) 8""" 144 | , """Data.Array.NonEmpty.(..) 8""" 145 | , """Data.Array.NonEmpty.(:) 6""" 146 | , """Data.Array.NonEmpty.(\\) 5""" 147 | , """Data.BooleanAlgebra.(&&) 3""" 148 | , """Data.BooleanAlgebra.(||) 2""" 149 | , """Data.Bounded.(<) 4""" 150 | , """Data.Bounded.(<=) 4""" 151 | , """Data.Bounded.(>) 4""" 152 | , """Data.Bounded.(>=) 4""" 153 | , """Data.CommutativeRing.(*) 7""" 154 | , """Data.CommutativeRing.(+) 6""" 155 | , """Data.CommutativeRing.(-) 6""" 156 | , """Data.DivisionRing.(*) 7""" 157 | , """Data.DivisionRing.(+) 6""" 158 | , """Data.DivisionRing.(-) 6""" 159 | , """Data.Either.Nested.(\/) type 6""" 160 | , """Data.Either.Nested.(\/) 6""" 161 | , """Data.Eq.(/=) 4""" 162 | , """Data.Eq.(==) 4""" 163 | , """Data.EuclideanRing.(*) 7""" 164 | , """Data.EuclideanRing.(+) 6""" 165 | , """Data.EuclideanRing.(-) 6""" 166 | , """Data.EuclideanRing.(/) 7""" 167 | , """Data.Field.(*) 7""" 168 | , """Data.Field.(+) 6""" 169 | , """Data.Field.(-) 6""" 170 | , """Data.Field.(/) 7""" 171 | , """Data.Function.(#) 1""" 172 | , """Data.Function.($) 0""" 173 | , """Data.Function.(<<<) 9""" 174 | , """Data.Function.(>>>) 9""" 175 | , """Data.Functor.($>) 4""" 176 | , """Data.Functor.(<#>) 1""" 177 | , """Data.Functor.(<$) 4""" 178 | , """Data.Functor.(<$>) 4""" 179 | , """Data.Functor.(<@>) 4""" 180 | , """Data.Functor.Contravariant.(>#<) 4""" 181 | , """Data.Functor.Contravariant.(>$<) 4""" 182 | , """Data.Functor.Coproduct.Nested.(<\/>) type 6""" 183 | , """Data.Functor.Coproduct.Nested.(<\/>) 6""" 184 | , """Data.Functor.Product.Nested.() type 6""" 185 | , """Data.Functor.Product.Nested.() 6""" 186 | , """Data.HeytingAlgebra.(&&) 3""" 187 | , """Data.HeytingAlgebra.(||) 2""" 188 | , """Data.HugeNum.(^) 8""" 189 | , """Data.Int.Bits.(.&.) 10""" 190 | , """Data.Int.Bits.(.^.) 10""" 191 | , """Data.Int.Bits.(.|.) 10""" 192 | , """Data.Lens.(%=) 4""" 193 | , """Data.Lens.(%~) 4""" 194 | , """Data.Lens.(&&&) 3""" 195 | , """Data.Lens.(&&=) 4""" 196 | , """Data.Lens.(&&~) 4""" 197 | , """Data.Lens.(***) 3""" 198 | , """Data.Lens.(*=) 4""" 199 | , """Data.Lens.(*~) 4""" 200 | , """Data.Lens.(+++) 2""" 201 | , """Data.Lens.(+=) 4""" 202 | , """Data.Lens.(+~) 4""" 203 | , """Data.Lens.(-=) 4""" 204 | , """Data.Lens.(-~) 4""" 205 | , """Data.Lens.(.=) 4""" 206 | , """Data.Lens.(.~) 4""" 207 | , """Data.Lens.(//=) 4""" 208 | , """Data.Lens.(//~) 4""" 209 | , """Data.Lens.(<>=) 4""" 210 | , """Data.Lens.(<>~) 4""" 211 | , """Data.Lens.(?=) 4""" 212 | , """Data.Lens.(?~) 4""" 213 | , """Data.Lens.(^.) 8""" 214 | , """Data.Lens.(^..) 8""" 215 | , """Data.Lens.(^?) 8""" 216 | , """Data.Lens.(||=) 4""" 217 | , """Data.Lens.(|||) 2""" 218 | , """Data.Lens.(||~) 4""" 219 | , """Data.Lens.Common.(&&&) 3""" 220 | , """Data.Lens.Common.(***) 3""" 221 | , """Data.Lens.Common.(+++) 2""" 222 | , """Data.Lens.Common.(|||) 2""" 223 | , """Data.Lens.Fold.(^..) 8""" 224 | , """Data.Lens.Fold.(^?) 8""" 225 | , """Data.Lens.Fold.Partial.(^?!) 8""" 226 | , """Data.Lens.Fold.Partial.(^@?!) 8""" 227 | , """Data.Lens.Getter.(^.) 8""" 228 | , """Data.Lens.Lens.Tuple.(&&&) 3""" 229 | , """Data.Lens.Lens.Tuple.(***) 3""" 230 | , """Data.Lens.Prism.Either.(+++) 2""" 231 | , """Data.Lens.Prism.Either.(|||) 2""" 232 | , """Data.Lens.Setter.(%=) 4""" 233 | , """Data.Lens.Setter.(%~) 4""" 234 | , """Data.Lens.Setter.(&&=) 4""" 235 | , """Data.Lens.Setter.(&&~) 4""" 236 | , """Data.Lens.Setter.(*=) 4""" 237 | , """Data.Lens.Setter.(*~) 4""" 238 | , """Data.Lens.Setter.(+=) 4""" 239 | , """Data.Lens.Setter.(+~) 4""" 240 | , """Data.Lens.Setter.(-=) 4""" 241 | , """Data.Lens.Setter.(-~) 4""" 242 | , """Data.Lens.Setter.(.=) 4""" 243 | , """Data.Lens.Setter.(.~) 4""" 244 | , """Data.Lens.Setter.(//=) 4""" 245 | , """Data.Lens.Setter.(//~) 4""" 246 | , """Data.Lens.Setter.(<>=) 4""" 247 | , """Data.Lens.Setter.(<>~) 4""" 248 | , """Data.Lens.Setter.(?=) 4""" 249 | , """Data.Lens.Setter.(?~) 4""" 250 | , """Data.Lens.Setter.(||=) 4""" 251 | , """Data.Lens.Setter.(||~) 4""" 252 | , """Data.List.(!!) 8""" 253 | , """Data.List.(..) 8""" 254 | , """Data.List.(:) 6""" 255 | , """Data.List.(\\) 5""" 256 | , """Data.List.Lazy.(!!) 8""" 257 | , """Data.List.Lazy.(..) 8""" 258 | , """Data.List.Lazy.(:) 6""" 259 | , """Data.List.Lazy.(\\) 5""" 260 | , """Data.List.Lazy.NonEmpty.(:) 6""" 261 | , """Data.List.Lazy.Types.(:) 6""" 262 | , """Data.List.NonEmpty.(!!) 8""" 263 | , """Data.List.NonEmpty.(:) 6""" 264 | , """Data.List.Types.(:) 6""" 265 | , """Data.Monoid.(<>) 5""" 266 | , """Data.NaturalTransformation.(~>) type 4""" 267 | , """Data.NonEmpty.(:|) 5""" 268 | , """Data.Number.(%) 7""" 269 | , """Data.Number.Approximate.(~=) 4""" 270 | , """Data.Number.Approximate.(≅) 4""" 271 | , """Data.Number.Approximate.(≇) 4""" 272 | , """Data.Options.(:=) 6""" 273 | , """Data.Ord.(<) 4""" 274 | , """Data.Ord.(<=) 4""" 275 | , """Data.Ord.(>) 4""" 276 | , """Data.Ord.(>=) 4""" 277 | , """Data.Profunctor.Choice.(+++) 2""" 278 | , """Data.Profunctor.Choice.(|||) 2""" 279 | , """Data.Profunctor.Strong.(&&&) 3""" 280 | , """Data.Profunctor.Strong.(***) 3""" 281 | , """Data.Ring.(*) 7""" 282 | , """Data.Ring.(+) 6""" 283 | , """Data.Ring.(-) 6""" 284 | , """Data.Semigroup.(<>) 5""" 285 | , """Data.Semiring.(*) 7""" 286 | , """Data.Semiring.(+) 6""" 287 | , """Data.Tuple.Nested.(/\) type 6""" 288 | , """Data.Tuple.Nested.(/\) 6""" 289 | , """Foreign.Index.(!) 9""" 290 | , """Parsing.Combinators.(!!) 8""" 291 | , """Parsing.Combinators.($>) 4""" 292 | , """Parsing.Combinators.(..) 8""" 293 | , """Parsing.Combinators.(:) 6""" 294 | , """Parsing.Combinators.(<#>) 1""" 295 | , """Parsing.Combinators.(<$) 4""" 296 | , """Parsing.Combinators.(<$>) 4""" 297 | , """Parsing.Combinators.() 4""" 298 | , """Parsing.Combinators.() 3""" 299 | , """Parsing.Combinators.(<@>) 4""" 300 | , """Parsing.Combinators.(<|>) 3""" 301 | , """Parsing.Combinators.(<~?>) 4""" 302 | , """Parsing.Combinators.(\\) 5""" 303 | , """Parsing.Indent.(<*/>) 11""" 304 | , """Parsing.Indent.(<+/>) 9""" 305 | , """Parsing.Indent.(<-/>) 10""" 306 | , """Parsing.Indent.() 12""" 307 | , """Pathy.(<..>) 6""" 308 | , """Pathy.(<.>) 6""" 309 | , """Pathy.() 6""" 310 | , """Pathy.Path.(<..>) 6""" 311 | , """Pathy.Path.(<.>) 6""" 312 | , """Pathy.Path.() 6""" 313 | , """Prelude.(~>) type 4""" 314 | , """Prelude.(#) 1""" 315 | , """Prelude.($) 0""" 316 | , """Prelude.($>) 4""" 317 | , """Prelude.(&&) 3""" 318 | , """Prelude.(*) 7""" 319 | , """Prelude.(*>) 4""" 320 | , """Prelude.(+) 6""" 321 | , """Prelude.(-) 6""" 322 | , """Prelude.(/) 7""" 323 | , """Prelude.(/=) 4""" 324 | , """Prelude.(<) 4""" 325 | , """Prelude.(<#>) 1""" 326 | , """Prelude.(<$) 4""" 327 | , """Prelude.(<$>) 4""" 328 | , """Prelude.(<*) 4""" 329 | , """Prelude.(<*>) 4""" 330 | , """Prelude.(<<<) 9""" 331 | , """Prelude.(<=) 4""" 332 | , """Prelude.(<=<) 1""" 333 | , """Prelude.(<>) 5""" 334 | , """Prelude.(<@>) 4""" 335 | , """Prelude.(=<<) 1""" 336 | , """Prelude.(==) 4""" 337 | , """Prelude.(>) 4""" 338 | , """Prelude.(>=) 4""" 339 | , """Prelude.(>=>) 1""" 340 | , """Prelude.(>>=) 1""" 341 | , """Prelude.(>>>) 9""" 342 | , """Prelude.(||) 2""" 343 | , """StringParser.() 4""" 344 | , """StringParser.Combinators.() 4""" 345 | , """Test.QuickCheck.(/==) 2""" 346 | , """Test.QuickCheck.(/=?) 2""" 347 | , """Test.QuickCheck.(<=?) 2""" 348 | , """Test.QuickCheck.() 2""" 350 | , """Test.QuickCheck.(===) 2""" 351 | , """Test.QuickCheck.(==?) 2""" 352 | , """Test.QuickCheck.(>=?) 2""" 353 | , """Test.QuickCheck.(>?) 2""" 354 | , """Type.Function.(#) type 1""" 355 | , """Type.Function.($) type 0""" 356 | , """Type.Prelude.(+) type 0""" 357 | , """Type.Row.(+) type 0""" 358 | ] 359 | -------------------------------------------------------------------------------- /src/Tidy/Precedence.purs: -------------------------------------------------------------------------------- 1 | module Tidy.Precedence 2 | ( QualifiedOperator(..) 3 | , OperatorNamespace(..) 4 | , OperatorTree(..) 5 | , OperatorChain 6 | , Precedence 7 | , PrecedenceMap 8 | , defaultPrecedence 9 | , toOperatorTree 10 | , remapOperators 11 | , insertOperator 12 | , lookupOperator 13 | , remapOperatorTo 14 | , remapModuleTo 15 | , remapModuleToHiding 16 | ) where 17 | 18 | import Prelude 19 | 20 | import Data.Array.NonEmpty (NonEmptyArray) 21 | import Data.Array.NonEmpty as NonEmptyArray 22 | import Data.Foldable (class Foldable, elem, foldMap, foldl) 23 | import Data.List (List(..), (:)) 24 | import Data.Map (Map) 25 | import Data.Map as Map 26 | import Data.Maybe (Maybe(..), fromMaybe, isJust) 27 | import Data.Tuple (Tuple(..), snd, uncurry) 28 | import PureScript.CST.Types (Declaration(..), FixityOp(..), Import(..), ImportDecl(..), Module(..), ModuleBody(..), ModuleHeader(..), ModuleName, Name(..), Operator, Separated(..), Wrapped(..)) 29 | 30 | data OperatorNamespace = OperatorType | OperatorValue 31 | 32 | derive instance eqOperatorNamespace :: Eq OperatorNamespace 33 | derive instance ordOperatorNamespace :: Ord OperatorNamespace 34 | 35 | data QualifiedOperator = QualifiedOperator (Maybe ModuleName) OperatorNamespace Operator 36 | 37 | derive instance eqQualifiedOperator :: Eq QualifiedOperator 38 | derive instance ordQualifiedOperator :: Ord QualifiedOperator 39 | 40 | type Precedence = Int 41 | 42 | defaultPrecedence :: Precedence 43 | defaultPrecedence = 10 44 | 45 | type PrecedenceMap = Map (Maybe ModuleName) (Map (Tuple OperatorNamespace Operator) Precedence) 46 | 47 | data OperatorTree op a 48 | = OpList (OperatorTree op a) Precedence (OperatorChain op a) 49 | | OpPure a 50 | 51 | data OperatorStk op a 52 | = OpHead (OperatorTree op a) 53 | | OpPrec (OperatorStk op a) Precedence (OperatorChain op a) 54 | 55 | type OperatorChain op a = NonEmptyArray (Tuple op (OperatorTree op a)) 56 | 57 | toOperatorTree 58 | :: forall op a 59 | . PrecedenceMap 60 | -> (op -> QualifiedOperator) 61 | -> a 62 | -> NonEmptyArray (Tuple op a) 63 | -> OperatorTree op a 64 | toOperatorTree precMap getOperator init = 65 | unwind <<< foldl go (OpHead (OpPure init)) 66 | where 67 | go :: OperatorStk op a -> Tuple op a -> OperatorStk op a 68 | go stk (Tuple op value) = do 69 | let QualifiedOperator modName opNs opName = getOperator op 70 | let prec = fromMaybe defaultPrecedence $ Map.lookup (Tuple opNs opName) =<< Map.lookup modName precMap 71 | let opCh = pure $ Tuple op $ OpPure value 72 | push stk $ pure $ Tuple prec opCh 73 | 74 | push :: forall op a. OperatorStk op a -> List (Tuple Precedence (OperatorChain op a)) -> OperatorStk op a 75 | push stk chs = case chs of 76 | Nil -> stk 77 | Tuple prec ops : Nil -> 78 | case stk of 79 | OpHead _ -> 80 | OpPrec stk prec ops 81 | OpPrec prevStk prevPrec prevOps -> 82 | case compare prec prevPrec of 83 | EQ -> OpPrec prevStk prevPrec (prevOps <> ops) 84 | GT -> OpPrec stk prec ops 85 | LT -> push prevStk (Tuple prevPrec prevOps : chs) 86 | Tuple nextPrec nextOps : next -> 87 | case stk of 88 | OpHead value -> 89 | push (OpHead (OpList value nextPrec nextOps)) next 90 | OpPrec prevStk prevPrec prevOps -> 91 | case compare nextPrec prevPrec of 92 | EQ -> push (OpPrec prevStk prevPrec (prevOps <> nextOps)) next 93 | GT -> push (OpPrec prevStk prevPrec (snoc prevOps nextPrec nextOps)) next 94 | LT -> push prevStk (Tuple prevPrec (snoc prevOps nextPrec nextOps) : next) 95 | 96 | unwind :: forall op a. OperatorStk op a -> OperatorTree op a 97 | unwind = case _ of 98 | OpHead value -> value 99 | OpPrec stk prec ops -> go prec ops stk 100 | where 101 | go prec ops = case _ of 102 | OpHead value -> OpList value prec ops 103 | OpPrec stk prevPrec prevOps -> 104 | go prevPrec (snoc prevOps prec ops) stk 105 | 106 | snoc :: forall op a. OperatorChain op a -> Precedence -> OperatorChain op a -> OperatorChain op a 107 | snoc prevOps nextPrec nextOps = do 108 | let { init, last: Tuple op value } = NonEmptyArray.unsnoc prevOps 109 | NonEmptyArray.snoc' init (Tuple op (OpList value nextPrec nextOps)) 110 | 111 | remapOperators :: forall e. PrecedenceMap -> Module e -> PrecedenceMap 112 | remapOperators = goModule 113 | where 114 | goModule precMap (Module { header: ModuleHeader { name: Name { name: modName }, imports }, body: ModuleBody { decls } }) = 115 | foldl (goDecl modName) (foldl goImportDecl precMap imports) decls 116 | 117 | goImportDecl precMap (ImportDecl { "module": Name { name: modName }, names, qualified }) = do 118 | let 119 | newModName = 120 | map (\(Tuple _ (Name { name })) -> name) qualified 121 | case names of 122 | Nothing -> 123 | remapModuleTo newModName modName precMap 124 | Just (Tuple hiding (Wrapped { value: Separated { head, tail } })) -> do 125 | let 126 | impOps = 127 | goImport modName head 128 | <> foldMap (goImport modName <<< snd) tail 129 | if isJust hiding then 130 | remapModuleToHiding impOps newModName modName precMap 131 | else 132 | foldl (flip (remapOperatorTo newModName)) precMap impOps 133 | 134 | goImport modName = case _ of 135 | ImportOp (Name { name: op }) -> 136 | [ QualifiedOperator (Just modName) OperatorValue op ] 137 | ImportTypeOp _ (Name { name: op }) -> 138 | [ QualifiedOperator (Just modName) OperatorType op ] 139 | _ -> 140 | [] 141 | 142 | goDecl modName precMap = case _ of 143 | DeclFixity { prec: Tuple _ prec, operator } -> 144 | case operator of 145 | FixityValue _ _ (Name { name: op }) -> 146 | precMap 147 | # insertOperator (QualifiedOperator Nothing OperatorValue op) prec 148 | # insertOperator (QualifiedOperator (Just modName) OperatorValue op) prec 149 | FixityType _ _ _ (Name { name: op }) -> 150 | precMap 151 | # insertOperator (QualifiedOperator Nothing OperatorType op) prec 152 | # insertOperator (QualifiedOperator (Just modName) OperatorType op) prec 153 | _ -> 154 | precMap 155 | 156 | insertOperator :: QualifiedOperator -> Precedence -> PrecedenceMap -> PrecedenceMap 157 | insertOperator (QualifiedOperator modName opNs op) prec = 158 | Map.alter 159 | case _ of 160 | Nothing -> 161 | Just $ Map.singleton opKey prec 162 | Just ops -> 163 | Just $ Map.insert opKey prec ops 164 | modName 165 | where 166 | opKey = Tuple opNs op 167 | 168 | lookupOperator :: QualifiedOperator -> PrecedenceMap -> Maybe Precedence 169 | lookupOperator (QualifiedOperator modName opNs op) precMap = 170 | Map.lookup modName precMap 171 | >>= Map.lookup (Tuple opNs op) 172 | 173 | remapOperatorTo :: Maybe ModuleName -> QualifiedOperator -> PrecedenceMap -> PrecedenceMap 174 | remapOperatorTo newModName qualOp@(QualifiedOperator _ opNs op) precMap = 175 | fromMaybe precMap do 176 | prec <- lookupOperator qualOp precMap 177 | pure $ insertOperator (QualifiedOperator newModName opNs op) prec precMap 178 | 179 | remapModuleTo :: Maybe ModuleName -> ModuleName -> PrecedenceMap -> PrecedenceMap 180 | remapModuleTo newModName modName precMap = 181 | fromMaybe precMap do 182 | ops <- Map.lookup (Just modName) precMap 183 | pure $ Map.alter 184 | case _ of 185 | Nothing -> 186 | Just ops 187 | Just oldOps -> 188 | Just (Map.union ops oldOps) 189 | newModName 190 | precMap 191 | 192 | remapModuleToHiding 193 | :: forall f 194 | . Foldable f 195 | => f QualifiedOperator 196 | -> Maybe ModuleName 197 | -> ModuleName 198 | -> PrecedenceMap 199 | -> PrecedenceMap 200 | remapModuleToHiding hiding newModName modName precMap = 201 | fromMaybe precMap do 202 | ops <- Map.lookup (Just modName) precMap 203 | let filteredOps = Map.filterKeys (not <<< flip elem hiding <<< uncurry (QualifiedOperator (Just modName))) ops 204 | pure $ Map.alter 205 | case _ of 206 | Nothing -> 207 | Just filteredOps 208 | Just oldOps -> 209 | Just (Map.union filteredOps oldOps) 210 | newModName 211 | precMap 212 | -------------------------------------------------------------------------------- /src/Tidy/Token.purs: -------------------------------------------------------------------------------- 1 | module Tidy.Token 2 | ( UnicodeOption(..) 3 | , printToken 4 | ) where 5 | 6 | import Prelude 7 | 8 | import Data.Maybe (Maybe(..)) 9 | import PureScript.CST.Types (ModuleName(..), SourceStyle(..), Token(..)) 10 | 11 | data UnicodeOption 12 | = UnicodeSource 13 | | UnicodeAlways 14 | | UnicodeNever 15 | 16 | derive instance eqUnicodeOption :: Eq UnicodeOption 17 | 18 | printUnicode :: String -> String -> SourceStyle -> UnicodeOption -> String 19 | printUnicode ascii uni style = case _ of 20 | UnicodeNever -> ascii 21 | UnicodeAlways -> uni 22 | UnicodeSource -> 23 | case style of 24 | ASCII -> ascii 25 | Unicode -> uni 26 | 27 | printToken :: UnicodeOption -> Token -> String 28 | printToken option = case _ of 29 | TokLeftParen -> 30 | "(" 31 | TokRightParen -> 32 | ")" 33 | TokLeftBrace -> 34 | "{" 35 | TokRightBrace -> 36 | "}" 37 | TokLeftSquare -> 38 | "[" 39 | TokRightSquare -> 40 | "]" 41 | TokLeftArrow style -> 42 | printUnicode "<-" "←" style option 43 | TokRightArrow style -> 44 | printUnicode "->" "→" style option 45 | TokRightFatArrow style -> 46 | printUnicode "=>" "⇒" style option 47 | TokDoubleColon style -> 48 | printUnicode "::" "∷" style option 49 | TokForall style -> 50 | printUnicode "forall" "∀" style option 51 | TokEquals -> 52 | "=" 53 | TokPipe -> 54 | "|" 55 | TokTick -> 56 | "`" 57 | TokDot -> 58 | "." 59 | TokComma -> 60 | "," 61 | TokUnderscore -> 62 | "_" 63 | TokBackslash -> 64 | "\\" 65 | TokAt -> 66 | "@" 67 | TokLowerName moduleName name -> 68 | printQualified moduleName name 69 | TokUpperName moduleName name -> 70 | printQualified moduleName name 71 | TokOperator moduleName name -> 72 | printQualified moduleName name 73 | TokSymbolName moduleName name -> 74 | printQualified moduleName ("(" <> name <> ")") 75 | TokSymbolArrow style -> 76 | printUnicode "(->)" "(→)" style option 77 | TokHole name -> 78 | "?" <> name 79 | TokChar raw _ -> 80 | "'" <> raw <> "'" 81 | TokString raw _ -> 82 | "\"" <> raw <> "\"" 83 | TokRawString raw -> 84 | "\"\"\"" <> raw <> "\"\"\"" 85 | TokInt raw _ -> 86 | raw 87 | TokNumber raw _ -> 88 | raw 89 | TokLayoutStart _ -> 90 | "" 91 | TokLayoutSep _ -> 92 | "" 93 | TokLayoutEnd _ -> 94 | "" 95 | 96 | printQualified :: Maybe ModuleName -> String -> String 97 | printQualified moduleName name = case moduleName of 98 | Nothing -> name 99 | Just (ModuleName mn) -> mn <> "." <> name 100 | -------------------------------------------------------------------------------- /src/Tidy/Util.purs: -------------------------------------------------------------------------------- 1 | module Tidy.Util where 2 | 3 | import Data.String.Regex as Regex 4 | import Data.String.Regex.Flags as Flags 5 | import Data.String.Regex.Unsafe (unsafeRegex) 6 | import PureScript.CST.Types (Labeled(..), Name(..)) 7 | 8 | splitLines :: String -> Array String 9 | splitLines = Regex.split (unsafeRegex """\r?\n""" Flags.global) 10 | 11 | splitStringEscapeLines :: String -> Array String 12 | splitStringEscapeLines = Regex.split (unsafeRegex """\\ *\r?\n\s*\\""" Flags.global) 13 | 14 | nameOf :: forall a. Name a -> a 15 | nameOf (Name { name }) = name 16 | 17 | overLabel :: forall a b c. (a -> b) -> Labeled a c -> Labeled b c 18 | overLabel k (Labeled lbl) = Labeled lbl { label = k lbl.label } 19 | -------------------------------------------------------------------------------- /test/FormatDirective.purs: -------------------------------------------------------------------------------- 1 | module Test.FormatDirective 2 | ( directiveRegex 3 | , FormatDirective 4 | , defaultFormat 5 | , SnapshotModule 6 | , parseDirectivesFromModule 7 | ) where 8 | 9 | import Prelude 10 | 11 | import ArgParse.Basic (ArgError, parseArgs, printArgError) 12 | import Bin.FormatOptions as Bin 13 | import Data.Array as Array 14 | import Data.Bifunctor (bimap) 15 | import Data.Either (Either(..)) 16 | import Data.Map (Map) 17 | import Data.Map as Map 18 | import Data.Maybe (Maybe(..), fromMaybe) 19 | import Data.Monoid (power) 20 | import Data.String as String 21 | import Data.String.Regex (Regex) 22 | import Data.String.Regex.Flags (global) 23 | import Data.String.Regex.Unsafe (unsafeRegex) 24 | import Data.Tuple (Tuple(..)) 25 | import Dodo (PrintOptions, twoSpaces) 26 | import PureScript.CST.Types (Comment(..), LineFeed, Module(..), ModuleHeader(..)) 27 | import Tidy (class FormatError, FormatOptions, defaultFormatOptions) 28 | 29 | directiveRegex :: Regex 30 | directiveRegex = unsafeRegex "\\n-- @format .+\\n" global 31 | 32 | type FormatDirective e a = 33 | { printOptions :: PrintOptions 34 | , formatOptions :: FormatOptions e a 35 | } 36 | 37 | defaultFormat :: forall e a. FormatError e => FormatDirective e a 38 | defaultFormat = 39 | { printOptions: twoSpaces { pageWidth = top :: Int } 40 | , formatOptions: defaultFormatOptions 41 | } 42 | 43 | type SnapshotModule e a = 44 | { module :: Module e 45 | , directives :: Map String (FormatDirective e a) 46 | } 47 | 48 | -- | Parse format directives from a module. The resulting module will have all 49 | -- | format directives stripped out. 50 | parseDirectivesFromModule :: forall e a. FormatError e => Module e -> SnapshotModule e a 51 | parseDirectivesFromModule (Module { header: ModuleHeader header, body }) = 52 | { module: moduleNoDirectives 53 | , directives 54 | } 55 | where 56 | directives :: Map String (FormatDirective e a) 57 | directives = 58 | Map.fromFoldable 59 | $ Array.mapMaybe parseOptionFromComment header.keyword.leadingComments 60 | 61 | -- Format directives are not considered part of the source code, so we 62 | -- strip them out of the input module. 63 | moduleNoDirectives :: Module e 64 | moduleNoDirectives = do 65 | let 66 | strippedComments = stripFormatDirectives header.keyword.leadingComments 67 | headerNoDirectives = ModuleHeader $ header { keyword { leadingComments = strippedComments } } 68 | 69 | Module { header: headerNoDirectives, body } 70 | 71 | stripFormatDirectives :: Array (Comment LineFeed) -> Array (Comment LineFeed) 72 | stripFormatDirectives input = case Array.uncons input of 73 | Nothing -> 74 | [] 75 | Just { head, tail } -> case parseOptionFromComment head of 76 | Nothing -> 77 | Array.cons head (stripFormatDirectives tail) 78 | Just _ -> case Array.uncons tail of 79 | Nothing -> 80 | [] 81 | -- If we strip the comment, we should strip the trailing line as well. 82 | Just { head: Line _ _, tail: tail' } -> 83 | stripFormatDirectives tail' 84 | Just _ -> 85 | stripFormatDirectives tail 86 | 87 | parseOptionFromComment :: Comment LineFeed -> Maybe (Tuple String (FormatDirective e a)) 88 | parseOptionFromComment = case _ of 89 | Comment original | String.contains (String.Pattern "@format") original -> do 90 | let input = String.split (String.Pattern " ") $ String.drop 11 $ String.trim original 91 | case parseFormatOptions input of 92 | Left _ -> Nothing 93 | Right directive -> pure $ Tuple original directive 94 | _ -> Nothing 95 | 96 | parseFormatOptions :: Array String -> Either String (FormatDirective e a) 97 | parseFormatOptions = bimap printArgError fromBinFormatOptions <<< parse 98 | where 99 | parse :: Array String -> Either ArgError Bin.FormatOptions 100 | parse = parseArgs "format-directives" "Parse format directives." Bin.formatOptions 101 | 102 | fromBinFormatOptions :: Bin.FormatOptions -> FormatDirective e a 103 | fromBinFormatOptions opts = 104 | { printOptions: 105 | { indentUnit: power " " opts.indent 106 | , indentWidth: opts.indent 107 | , pageWidth: fromMaybe top opts.width 108 | , ribbonRatio: opts.ribbon 109 | } 110 | , formatOptions: 111 | { formatError: default.formatError 112 | , operators: default.operators 113 | , unicode: opts.unicode 114 | , typeArrowPlacement: opts.typeArrowPlacement 115 | , importSort: opts.importSort 116 | , importWrap: opts.importWrap 117 | } 118 | } 119 | where 120 | default :: FormatOptions e a 121 | default = defaultFormatOptions 122 | -------------------------------------------------------------------------------- /test/Main.purs: -------------------------------------------------------------------------------- 1 | module Test.Main where 2 | 3 | import Prelude 4 | 5 | import Ansi.Output (foreground, withGraphics) 6 | import Data.Array as Array 7 | import Data.Foldable (any, findMap, fold, for_) 8 | import Data.Map as Map 9 | import Data.Maybe (Maybe(..)) 10 | import Data.String (Pattern(..)) 11 | import Data.String as String 12 | import Data.String.CodeUnits as SCU 13 | import Data.Tuple (Tuple(..)) 14 | import Dodo.Ansi (Color(..)) 15 | import Effect (Effect) 16 | import Effect.Aff (Aff, launchAff_) 17 | import Effect.Class (liftEffect) 18 | import Effect.Class.Console as Console 19 | import Effect.Exception as Error 20 | import Node.Process as Process 21 | import Test.Snapshot (SnapshotResult(..), SnapshotResultGroup(..), snapshotFormat) 22 | 23 | main :: Effect Unit 24 | main = do 25 | args <- Process.argv 26 | let accept = any (eq "--accept" || eq "-a") args 27 | let printOutput = any (eq "--print-output" || eq "-p") args 28 | let filter = Pattern <$> findMap (String.stripPrefix (Pattern "--filter=")) args 29 | let printResult = printResultGroup printOutput 30 | launchAff_ do 31 | results@(SnapshotResultGroup { hasBad }) <- snapshotFormat accept filter 32 | printResult 0 results 33 | when hasBad do 34 | liftEffect $ Process.setExitCode 1 35 | where 36 | indent :: Int -> String 37 | indent = fold <<< flip Array.replicate " " 38 | 39 | printResultGroup :: Boolean -> Int -> SnapshotResultGroup -> Aff Unit 40 | printResultGroup printOutput lvl (SnapshotResultGroup { results, nested }) = do 41 | let 42 | logIndented :: Int -> String -> Aff Unit 43 | logIndented lvl' = Console.log <<< String.joinWith "\n" <<< map (append (indent lvl')) <<< String.split (Pattern "\n") 44 | 45 | for_ results \{ name, results: outputResults } -> do 46 | logIndented lvl $ "Checking " <> name 47 | for_ outputResults \{ output, result, directive } -> case result of 48 | Passed -> do 49 | logIndented (lvl + 1) $ withGraphics (foreground Green) "✓" <> " " <> directive <> " passed." 50 | when printOutput $ logIndented (lvl + 2) output 51 | Saved -> do 52 | logIndented (lvl + 1) $ withGraphics (foreground Yellow) "✓" <> " " <> directive <> " saved." 53 | when printOutput $ logIndented (lvl + 2) output 54 | Accepted -> do 55 | logIndented (lvl + 1) $ withGraphics (foreground Yellow) "✓" <> " " <> directive <> " accepted." 56 | when printOutput $ logIndented (lvl + 2) output 57 | Failed diff -> do 58 | logIndented (lvl + 1) $ withGraphics (foreground Red) "✗" <> " " <> directive <> " failed." 59 | logIndented (lvl + 2) diff 60 | when printOutput $ logIndented (lvl + 2) output 61 | ErrorRunningTest err -> do 62 | logIndented (lvl + 1) $ withGraphics (foreground Red) "✗" <> " " <> directive <> " failed due to an error." 63 | logIndented (lvl + 2) $ Error.message err 64 | 65 | for_ (Map.toUnfoldable nested :: Array (Tuple String SnapshotResultGroup)) \(Tuple path group) -> do 66 | logIndented lvl "=================" 67 | logIndented lvl (correctCase path) 68 | printResultGroup printOutput (lvl + 1) group 69 | 70 | correctCase :: String -> String 71 | correctCase = 72 | String.split (Pattern "-") 73 | >>> map upperFirstChar 74 | >>> String.joinWith " " 75 | 76 | upperFirstChar :: String -> String 77 | upperFirstChar s = case Array.uncons (SCU.toCharArray s) of 78 | Nothing -> "" 79 | Just { head, tail } -> String.toUpper (SCU.fromCharArray (Array.singleton head)) <> SCU.fromCharArray tail 80 | -------------------------------------------------------------------------------- /test/Snapshot.purs: -------------------------------------------------------------------------------- 1 | module Test.Snapshot where 2 | 3 | import Prelude 4 | 5 | import Control.Alternative (guard) 6 | import Data.Array (dropEnd, mapMaybe) 7 | import Data.Array as Array 8 | import Data.Array.NonEmpty as NEA 9 | import Data.Either (Either(..)) 10 | import Data.Foldable (foldMap, foldl) 11 | import Data.FoldableWithIndex (foldMapWithIndex) 12 | import Data.Map (Map) 13 | import Data.Map as Map 14 | import Data.Maybe (Maybe(..), fromMaybe) 15 | import Data.Posix.Signal (Signal(..)) 16 | import Data.Set as Set 17 | import Data.String (Pattern(..), Replacement(..), split, stripSuffix) 18 | import Data.String as String 19 | import Data.String.Regex as Regex 20 | import Data.Traversable (for) 21 | import Data.Tuple (Tuple(..), fst) 22 | import Dodo (PrintOptions) 23 | import Dodo as Dodo 24 | import Effect (Effect) 25 | import Effect.Aff (Aff, Error, catchError, effectCanceler, makeAff, try) 26 | import Effect.Class (liftEffect) 27 | import Effect.Exception (error) 28 | import Node.Buffer (Buffer, freeze) 29 | import Node.Buffer as Buffer 30 | import Node.Buffer.Immutable as ImmutableBuffer 31 | import Node.ChildProcess (ExecResult) 32 | import Node.ChildProcess as ChildProcess 33 | import Node.ChildProcess.Types (customShell) 34 | import Node.Encoding (Encoding(..)) 35 | import Node.FS.Aff (readFile, writeFile) 36 | import Node.Glob.Basic (expandGlobs) 37 | import Node.Path (FilePath) 38 | import PureScript.CST (RecoveredParserResult(..), parseModule) 39 | import PureScript.CST.Errors (printParseError) 40 | import PureScript.CST.Types (Module) 41 | import Test.FormatDirective (defaultFormat, directiveRegex, parseDirectivesFromModule) 42 | import Tidy (class FormatError, FormatOptions) 43 | import Tidy as Tidy 44 | import Tidy.Operators (parseOperatorTable) 45 | import Tidy.Operators.Defaults (defaultOperators) 46 | import Tidy.Precedence (PrecedenceMap) 47 | 48 | data SnapshotResult 49 | = Passed 50 | | Saved 51 | | Accepted 52 | | Failed String 53 | | ErrorRunningTest Error 54 | 55 | type SnapshotTest = 56 | { name :: String 57 | , results :: Array { output :: String, result :: SnapshotResult, directive :: String } 58 | } 59 | 60 | isBad :: SnapshotResult -> Boolean 61 | isBad = case _ of 62 | Failed _ -> true 63 | ErrorRunningTest _ -> true 64 | _ -> false 65 | 66 | newtype SnapshotResultGroup = SnapshotResultGroup 67 | { results :: Array SnapshotTest 68 | , nested :: Map String SnapshotResultGroup 69 | , hasBad :: Boolean 70 | } 71 | 72 | inputSuffix :: String 73 | inputSuffix = ".input" 74 | 75 | outputSuffix :: String 76 | outputSuffix = ".output" 77 | 78 | snapshotPath :: String 79 | snapshotPath = "test/snapshots" 80 | 81 | snapshotDirectory :: { path :: String, trimPath :: String -> String } 82 | snapshotDirectory = 83 | { path: "./" <> snapshotPath 84 | , trimPath: String.replace (Pattern (snapshotPath <> "/")) (Replacement "") 85 | } 86 | 87 | snapshotFormat :: Boolean -> Maybe Pattern -> Aff SnapshotResultGroup 88 | snapshotFormat accept mbPattern = do 89 | paths <- mapMaybe goPath <<< Array.fromFoldable <$> expandGlobs snapshotDirectory.path [ "**/*" <> inputSuffix ] 90 | tested <- for paths runSnapshot 91 | pure $ groupSnapshots tested 92 | where 93 | groupSnapshots :: Array (Tuple (Array String) SnapshotTest) -> SnapshotResultGroup 94 | groupSnapshots = 95 | Array.sortWith fst 96 | >>> foldl addToGroup (emptyGroup false) 97 | 98 | addToGroup :: SnapshotResultGroup -> Tuple (Array String) SnapshotTest -> SnapshotResultGroup 99 | addToGroup (SnapshotResultGroup { results, nested, hasBad }) (Tuple path result) = do 100 | let 101 | badResult = Array.any (_.result >>> isBad) result.results 102 | case Array.uncons path of 103 | Nothing -> 104 | SnapshotResultGroup 105 | { results: Array.snoc results result 106 | , nested 107 | , hasBad: hasBad || badResult 108 | } 109 | Just { head, tail } -> 110 | SnapshotResultGroup 111 | { results 112 | , nested: Map.alter (fromMaybe (emptyGroup badResult) >>> flip addToGroup (Tuple tail result) >>> Just) head nested 113 | , hasBad: hasBad || badResult 114 | } 115 | 116 | emptyGroup :: Boolean -> SnapshotResultGroup 117 | emptyGroup hasBad = SnapshotResultGroup 118 | { results: [] 119 | , nested: Map.empty 120 | , hasBad 121 | } 122 | 123 | goPath :: String -> Maybe { path :: FilePath, name :: String } 124 | goPath path = do 125 | let 126 | splitPath = split (Pattern "/") path 127 | name <- filterPath =<< stripSuffix (Pattern inputSuffix) =<< Array.last splitPath 128 | pure { path, name } 129 | 130 | filterPath = case mbPattern of 131 | Just pat -> 132 | \path -> 133 | guard (String.contains pat path) $> path 134 | Nothing -> 135 | pure 136 | 137 | makeErrorResult :: String -> Error -> Aff SnapshotTest 138 | makeErrorResult name err = pure { name, results: [ { output: "", result: ErrorRunningTest err, directive: "" } ] } 139 | 140 | runSnapshot :: { path :: FilePath, name :: String } -> Aff (Tuple (Array String) SnapshotTest) 141 | runSnapshot { path, name } = flip catchError (map (Tuple testPath) <<< makeErrorResult name) do 142 | let outputPath = String.replace (Pattern inputSuffix) (Replacement outputSuffix) path 143 | contents <- liftEffect <<< bufferToUTF8 =<< readFile path 144 | case parseModule contents of 145 | ParseSucceeded mod -> 146 | Tuple testPath <$> runSnapshotForModule name outputPath mod 147 | ParseSucceededWithErrors mod _ -> 148 | Tuple testPath <$> runSnapshotForModule name outputPath mod 149 | ParseFailed err -> do 150 | let 151 | formattedError = 152 | printParseError err.error 153 | <> " at " 154 | <> show (err.position.line + 1) 155 | <> ":" 156 | <> show (err.position.column + 1) 157 | pure (Tuple testPath { name, results: [ { output: "", result: Failed formattedError, directive: "" } ] }) 158 | where 159 | testPath :: Array String 160 | testPath = dropEnd 1 $ String.split (Pattern "/") $ snapshotDirectory.trimPath path 161 | 162 | runSnapshotForModule :: forall e. FormatError e => String -> FilePath -> Module e -> Aff SnapshotTest 163 | runSnapshotForModule name outputPath mod = do 164 | let 165 | inputModule = parseDirectivesFromModule mod 166 | 167 | formatModuleWith { printOptions, formatOptions } = 168 | formatModule printOptions formatOptions inputModule.module 169 | 170 | defaultFormattedModule = 171 | formatModuleWith defaultFormat 172 | 173 | snapshotOutputs = 174 | Array.cons (Tuple "Default formatting" defaultFormattedModule) 175 | $ map (\(Tuple s d) -> Tuple s (formatModuleWith d)) 176 | $ Map.toUnfoldable inputModule.directives 177 | 178 | snapshotOutputFileContents = Array.fold 179 | [ defaultFormattedModule 180 | , inputModule.directives # foldMapWithIndex \directiveSource directive -> 181 | "\n" 182 | <> directiveSource 183 | <> "\n" 184 | <> formatModuleWith directive 185 | ] 186 | 187 | acceptOutput = 188 | writeFile outputPath 189 | =<< liftEffect (Buffer.fromString snapshotOutputFileContents UTF8) 190 | 191 | savedOutputFile <- try $ readFile outputPath 192 | case savedOutputFile of 193 | Left _ -> do 194 | acceptOutput 195 | pure 196 | { name 197 | , results: map (\(Tuple directive output) -> { result: Saved, output, directive }) snapshotOutputs 198 | } 199 | Right buffer -> do 200 | storedOutput <- liftEffect $ bufferToUTF8 buffer 201 | let 202 | storedOutputDirectives :: Array String 203 | storedOutputDirectives = 204 | storedOutput 205 | # Regex.match directiveRegex 206 | # foldMap (NEA.toUnfoldable >>> Array.catMaybes) 207 | # map String.trim 208 | # Array.mapMaybe (\input -> if String.contains (String.Pattern "@format") input then Just input else Nothing) 209 | 210 | matchedOutputs = 211 | storedOutput 212 | # Regex.split directiveRegex 213 | # Array.zip snapshotOutputs 214 | 215 | checkOutput :: Tuple (Tuple String String) String -> Aff { output :: String, result :: SnapshotResult, directive :: String } 216 | checkOutput (Tuple (Tuple directive output) saved) = 217 | if output == saved then 218 | pure { output, result: Passed, directive } 219 | else if accept then do 220 | acceptOutput 221 | pure { output, result: Accepted, directive } 222 | else do 223 | let 224 | diffCmd = "diff <(echo \"" <> output <> "\") <(echo \"" <> saved <> "\")" 225 | { stdout: diffBuff } <- exec diffCmd 226 | diffOutput <- liftEffect $ bufferToUTF8 diffBuff 227 | pure { output, result: Failed diffOutput, directive } 228 | 229 | acceptedOutput :: Tuple String String -> Aff { output :: String, result :: SnapshotResult, directive :: String } 230 | acceptedOutput (Tuple directive output) = 231 | pure { output, result: Accepted, directive } 232 | 233 | if storedOutputDirectives == Set.toUnfoldable (Map.keys inputModule.directives) then do 234 | results <- for matchedOutputs checkOutput 235 | pure { name, results } 236 | else if accept then do 237 | acceptOutput 238 | results <- for snapshotOutputs acceptedOutput 239 | pure { name, results } 240 | else 241 | makeErrorResult name (error "Mismatched format options in output file.") 242 | 243 | formatModule :: forall e a. PrintOptions -> FormatOptions e a -> Module e -> String 244 | formatModule opts conf = Dodo.print Dodo.plainText opts <<< Tidy.toDoc <<< Tidy.formatModule (conf { operators = operators }) 245 | 246 | operators :: PrecedenceMap 247 | operators = parseOperatorTable defaultOperators 248 | 249 | exec :: String -> Aff ExecResult 250 | exec command = makeAff \k -> do 251 | childProc <- ChildProcess.exec' command (_ { shell = Just (customShell "/bin/bash") }) (k <<< pure) 252 | pure $ effectCanceler $ void $ ChildProcess.killSignal SIGABRT childProc 253 | 254 | bufferToUTF8 :: Buffer -> Effect String 255 | bufferToUTF8 = map (ImmutableBuffer.toString UTF8) <<< freeze 256 | -------------------------------------------------------------------------------- /test/snapshots/.gitattributes: -------------------------------------------------------------------------------- 1 | *.output -merge -text 2 | -------------------------------------------------------------------------------- /test/snapshots/Array.input: -------------------------------------------------------------------------------- 1 | module Array where 2 | 3 | records :: Array { foo :: Int, bar :: String } 4 | records = [ { foo: 10, bar: "Hello" } 5 | , 6 | { foo: 7 | 10 8 | , bar: "Hello world"}, 9 | { foo: 10 10 | , bar: "Goodbye" 11 | } 12 | ] 13 | 14 | ints :: Array Int 15 | ints = [ 10, 9, 8, 7, 6, 5 ] 16 | 17 | bools :: Array Boolean 18 | bools = 19 | [ (a && b || c) 20 | , (a && 21 | (b || ( 22 | c && d))) 23 | , (a && ( 24 | c && d))] 25 | 26 | strings :: Array String 27 | strings 28 | = ["hello", "world", 29 | "strings" 30 | , "go", 31 | """ 32 | Multiline string 33 | """ 34 | ] 35 | 36 | nested :: Array (Array Int) 37 | nested = 38 | [ [ [], [], [ 1, 2, 3 ]], 39 | [ [ 1, 2 40 | , 3, 41 | 4 ]] 42 | , 43 | [ [ 1], 44 | [1] 45 | , [ 1 46 | , 2, 47 | 3, 4 48 | , 5]] 49 | ] 50 | -------------------------------------------------------------------------------- /test/snapshots/Array.output: -------------------------------------------------------------------------------- 1 | module Array where 2 | 3 | records :: Array { foo :: Int, bar :: String } 4 | records = 5 | [ { foo: 10, bar: "Hello" } 6 | , { foo: 7 | 10 8 | , bar: "Hello world" 9 | } 10 | , { foo: 10 11 | , bar: "Goodbye" 12 | } 13 | ] 14 | 15 | ints :: Array Int 16 | ints = [ 10, 9, 8, 7, 6, 5 ] 17 | 18 | bools :: Array Boolean 19 | bools = 20 | [ (a && b || c) 21 | , ( a && 22 | ( b || 23 | ( c && d 24 | ) 25 | ) 26 | ) 27 | , ( a && 28 | ( c && d 29 | ) 30 | ) 31 | ] 32 | 33 | strings :: Array String 34 | strings = 35 | [ "hello" 36 | , "world" 37 | , "strings" 38 | , "go" 39 | , """ 40 | Multiline string 41 | """ 42 | ] 43 | 44 | nested :: Array (Array Int) 45 | nested = 46 | [ [ [], [], [ 1, 2, 3 ] ] 47 | , [ [ 1 48 | , 2 49 | , 3 50 | , 4 51 | ] 52 | ] 53 | , [ [ 1 ] 54 | , [ 1 ] 55 | , [ 1 56 | , 2 57 | , 3 58 | , 4 59 | , 5 60 | ] 61 | ] 62 | ] 63 | -------------------------------------------------------------------------------- /test/snapshots/BlockComments.input: -------------------------------------------------------------------------------- 1 | module BlockComments where 2 | 3 | {- test -} 4 | 5 | test = 6 | {- 7 | test 8 | test 9 | test 10 | -} 11 | 42 12 | 13 | 14 | test = 15 | {- test 16 | test 17 | test 18 | test 19 | test -} 20 | 42 21 | 22 | test = 23 | {- 24 | test 25 | 26 | test 27 | 28 | test 29 | -} 30 | 42 31 | -------------------------------------------------------------------------------- /test/snapshots/BlockComments.output: -------------------------------------------------------------------------------- 1 | module BlockComments where 2 | 3 | {- test -} 4 | 5 | test = 6 | {- 7 | test 8 | test 9 | test 10 | -} 11 | 42 12 | 13 | test = 14 | {- test 15 | test 16 | test 17 | test 18 | test -} 19 | 42 20 | 21 | test = 22 | {- 23 | test 24 | 25 | test 26 | 27 | test 28 | -} 29 | 42 30 | -------------------------------------------------------------------------------- /test/snapshots/DataDeclarations.input: -------------------------------------------------------------------------------- 1 | -- @format --width=40 2 | module DataDeclarations where 3 | 4 | data Test a = A | B | C | D | E | F | G | H | I | J | K | L 5 | 6 | data Test a 7 | = A | B | C | D | E 8 | 9 | data Test a 10 | = A | B 11 | | C | D | E 12 | 13 | data Test a = 14 | A | B 15 | | C | D | E 16 | 17 | newtype Test a = Test a 18 | 19 | newtype Test a 20 | = Test a 21 | 22 | newtype Test a = 23 | Test a 24 | 25 | newtype Test a 26 | = 27 | Test a 28 | 29 | type Test a = a 30 | 31 | type Test a 32 | = a 33 | 34 | type Test a = 35 | a 36 | 37 | type Test a 38 | = 39 | a 40 | -------------------------------------------------------------------------------- /test/snapshots/DataDeclarations.output: -------------------------------------------------------------------------------- 1 | module DataDeclarations where 2 | 3 | data Test a = A | B | C | D | E | F | G | H | I | J | K | L 4 | 5 | data Test a 6 | = A 7 | | B 8 | | C 9 | | D 10 | | E 11 | 12 | data Test a 13 | = A 14 | | B 15 | | C 16 | | D 17 | | E 18 | 19 | data Test a 20 | = A 21 | | B 22 | | C 23 | | D 24 | | E 25 | 26 | newtype Test a = Test a 27 | 28 | newtype Test a = Test a 29 | 30 | newtype Test a = 31 | Test a 32 | 33 | newtype Test a = 34 | Test a 35 | 36 | type Test a = a 37 | 38 | type Test a = a 39 | 40 | type Test a = 41 | a 42 | 43 | type Test a = 44 | a 45 | 46 | -- @format --width=40 47 | module DataDeclarations where 48 | 49 | data Test a 50 | = A 51 | | B 52 | | C 53 | | D 54 | | E 55 | | F 56 | | G 57 | | H 58 | | I 59 | | J 60 | | K 61 | | L 62 | 63 | data Test a 64 | = A 65 | | B 66 | | C 67 | | D 68 | | E 69 | 70 | data Test a 71 | = A 72 | | B 73 | | C 74 | | D 75 | | E 76 | 77 | data Test a 78 | = A 79 | | B 80 | | C 81 | | D 82 | | E 83 | 84 | newtype Test a = Test a 85 | 86 | newtype Test a = Test a 87 | 88 | newtype Test a = 89 | Test a 90 | 91 | newtype Test a = 92 | Test a 93 | 94 | type Test a = a 95 | 96 | type Test a = a 97 | 98 | type Test a = 99 | a 100 | 101 | type Test a = 102 | a 103 | -------------------------------------------------------------------------------- /test/snapshots/DeclarationBreaks.input: -------------------------------------------------------------------------------- 1 | -- @format --width 40 2 | module DeclarationBreaks where 3 | import Foo 4 | import Bar 5 | type Ok = String 6 | type Ok2 :: Type 7 | type Ok2 = String 8 | -- Comment 9 | type Ok3 = String 10 | type Ok4 = Int 11 | type OK5 = Int 12 | class Foo a where foo :: a 13 | instance Foo Int where foo = 12 14 | else instance Foo String where foo = "foo" 15 | test :: Int 16 | test = 42 17 | where 18 | a = 19 | 1 20 | b = 2 21 | c :: Int 22 | c = 23 | let d :: Int 24 | d = 3 25 | e = 4 26 | in do 27 | let f = 28 | 5 29 | g :: Int 30 | g = 6 31 | g 32 | test2 Foo = 33 | 1 34 | test2 Bar = 35 | 2 36 | test2 Baz = 3 37 | data Wat = Wat1 | Wat2 | Wat3 | Wat4 | Wat5 | Wat6 | Wat7 | Wat8 | Wat9 38 | data Ohh = Ohh 39 | -------------------------------------------------------------------------------- /test/snapshots/DeclarationBreaks.output: -------------------------------------------------------------------------------- 1 | module DeclarationBreaks where 2 | 3 | import Foo 4 | import Bar 5 | 6 | type Ok = String 7 | 8 | type Ok2 :: Type 9 | type Ok2 = String 10 | 11 | -- Comment 12 | type Ok3 = String 13 | type Ok4 = Int 14 | type OK5 = Int 15 | 16 | class Foo a where 17 | foo :: a 18 | 19 | instance Foo Int where 20 | foo = 12 21 | else instance Foo String where 22 | foo = "foo" 23 | 24 | test :: Int 25 | test = 42 26 | where 27 | a = 28 | 1 29 | b = 2 30 | 31 | c :: Int 32 | c = 33 | let 34 | d :: Int 35 | d = 3 36 | e = 4 37 | in 38 | do 39 | let 40 | f = 41 | 5 42 | 43 | g :: Int 44 | g = 6 45 | g 46 | 47 | test2 Foo = 48 | 1 49 | test2 Bar = 50 | 2 51 | test2 Baz = 3 52 | 53 | data Wat = Wat1 | Wat2 | Wat3 | Wat4 | Wat5 | Wat6 | Wat7 | Wat8 | Wat9 54 | data Ohh = Ohh 55 | 56 | -- @format --width 40 57 | module DeclarationBreaks where 58 | 59 | import Foo 60 | import Bar 61 | 62 | type Ok = String 63 | 64 | type Ok2 :: Type 65 | type Ok2 = String 66 | 67 | -- Comment 68 | type Ok3 = String 69 | type Ok4 = Int 70 | type OK5 = Int 71 | 72 | class Foo a where 73 | foo :: a 74 | 75 | instance Foo Int where 76 | foo = 12 77 | else instance Foo String where 78 | foo = "foo" 79 | 80 | test :: Int 81 | test = 42 82 | where 83 | a = 84 | 1 85 | b = 2 86 | 87 | c :: Int 88 | c = 89 | let 90 | d :: Int 91 | d = 3 92 | e = 4 93 | in 94 | do 95 | let 96 | f = 97 | 5 98 | 99 | g :: Int 100 | g = 6 101 | g 102 | 103 | test2 Foo = 104 | 1 105 | test2 Bar = 106 | 2 107 | test2 Baz = 3 108 | 109 | data Wat 110 | = Wat1 111 | | Wat2 112 | | Wat3 113 | | Wat4 114 | | Wat5 115 | | Wat6 116 | | Wat7 117 | | Wat8 118 | | Wat9 119 | 120 | data Ohh = Ohh 121 | -------------------------------------------------------------------------------- /test/snapshots/DeclarationSignatures.input: -------------------------------------------------------------------------------- 1 | -- @format --arrow-last 2 | module DeclarationSignatures where 3 | 4 | test :: { foo :: Int 5 | , bar :: Int 6 | } 7 | 8 | test 9 | :: { foo :: Int 10 | , bar :: Int 11 | } 12 | 13 | test :: 14 | Int -> Int 15 | 16 | test :: { foo :: Int 17 | , bar :: Int 18 | } -> Int 19 | 20 | foo 21 | -- wat 22 | = Foo 23 | { bar: 42 24 | } 25 | -------------------------------------------------------------------------------- /test/snapshots/DeclarationSignatures.output: -------------------------------------------------------------------------------- 1 | module DeclarationSignatures where 2 | 3 | test 4 | :: { foo :: Int 5 | , bar :: Int 6 | } 7 | 8 | test 9 | :: { foo :: Int 10 | , bar :: Int 11 | } 12 | 13 | test 14 | :: Int -> Int 15 | 16 | test 17 | :: { foo :: Int 18 | , bar :: Int 19 | } 20 | -> Int 21 | 22 | foo 23 | -- wat 24 | = Foo 25 | { bar: 42 26 | } 27 | 28 | -- @format --arrow-last 29 | module DeclarationSignatures where 30 | 31 | test :: 32 | { foo :: Int 33 | , bar :: Int 34 | } 35 | 36 | test :: 37 | { foo :: Int 38 | , bar :: Int 39 | } 40 | 41 | test :: 42 | Int -> Int 43 | 44 | test :: 45 | { foo :: Int 46 | , bar :: Int 47 | } -> 48 | Int 49 | 50 | foo 51 | -- wat 52 | = Foo 53 | { bar: 42 54 | } 55 | -------------------------------------------------------------------------------- /test/snapshots/DelimitersWithLeadingComments.input: -------------------------------------------------------------------------------- 1 | module DelimitersWithLeadingComments where 2 | 3 | test = 4 | -- Array 5 | [ a, b, c ] 6 | 7 | test = 8 | -- Array 9 | [ a, b 10 | , c ] 11 | 12 | test = 13 | -- Record 14 | { a, b, c } 15 | 16 | test = 17 | -- Record 18 | { a, b 19 | , c } 20 | 21 | test = 22 | -- Parens 23 | (a b c) 24 | 25 | test = 26 | -- Parens 27 | (a b 28 | c) 29 | -------------------------------------------------------------------------------- /test/snapshots/DelimitersWithLeadingComments.output: -------------------------------------------------------------------------------- 1 | module DelimitersWithLeadingComments where 2 | 3 | test = 4 | -- Array 5 | [ a, b, c ] 6 | 7 | test = 8 | -- Array 9 | [ a 10 | , b 11 | , c 12 | ] 13 | 14 | test = 15 | -- Record 16 | { a, b, c } 17 | 18 | test = 19 | -- Record 20 | { a 21 | , b 22 | , c 23 | } 24 | 25 | test = 26 | -- Parens 27 | (a b c) 28 | 29 | test = 30 | -- Parens 31 | ( a b 32 | c 33 | ) 34 | -------------------------------------------------------------------------------- /test/snapshots/Exports.input: -------------------------------------------------------------------------------- 1 | module Exports (module A, class B 2 | , type (||), C 3 | (D), E(..), (||), fun) where 4 | -------------------------------------------------------------------------------- /test/snapshots/Exports.output: -------------------------------------------------------------------------------- 1 | module Exports 2 | ( module A 3 | , class B 4 | , type (||) 5 | , C 6 | ( D 7 | ) 8 | , E(..) 9 | , (||) 10 | , fun 11 | ) where 12 | -------------------------------------------------------------------------------- /test/snapshots/ForeignImportSignatures.input: -------------------------------------------------------------------------------- 1 | -- @format --arrow-last 2 | module ForeignImportSignatures where 3 | 4 | foreign import test :: Foo -> Bar 5 | 6 | foreign import test :: Foo 7 | -> Bar 8 | 9 | foreign import test 10 | 11 | 12 | :: Foo 13 | { bar :: Int 14 | } 15 | 16 | foreign import test :: Foo 17 | { bar :: Int 18 | } 19 | 20 | foreign import test :: 21 | Foo 22 | { bar :: Int 23 | } 24 | 25 | foreign import test :: 26 | Foo 27 | { bar :: Int 28 | } 29 | -> Foo 30 | { bar :: Int 31 | } 32 | 33 | foreign import test 34 | :: Foo 35 | { bar :: Int 36 | } 37 | -> Foo 38 | { bar :: Int 39 | } 40 | 41 | foreign import test 42 | :: { foo :: Int 43 | , bar :: Int 44 | } 45 | -------------------------------------------------------------------------------- /test/snapshots/ForeignImportSignatures.output: -------------------------------------------------------------------------------- 1 | module ForeignImportSignatures where 2 | 3 | foreign import test :: Foo -> Bar 4 | 5 | foreign import test 6 | :: Foo 7 | -> Bar 8 | 9 | foreign import test 10 | :: Foo 11 | { bar :: Int 12 | } 13 | 14 | foreign import test 15 | :: Foo 16 | { bar :: Int 17 | } 18 | 19 | foreign import test 20 | :: Foo 21 | { bar :: Int 22 | } 23 | 24 | foreign import test 25 | :: Foo 26 | { bar :: Int 27 | } 28 | -> Foo 29 | { bar :: Int 30 | } 31 | 32 | foreign import test 33 | :: Foo 34 | { bar :: Int 35 | } 36 | -> Foo 37 | { bar :: Int 38 | } 39 | 40 | foreign import test 41 | :: { foo :: Int 42 | , bar :: Int 43 | } 44 | 45 | -- @format --arrow-last 46 | module ForeignImportSignatures where 47 | 48 | foreign import test :: Foo -> Bar 49 | 50 | foreign import test :: 51 | Foo -> 52 | Bar 53 | 54 | foreign import test :: 55 | Foo 56 | { bar :: Int 57 | } 58 | 59 | foreign import test :: 60 | Foo 61 | { bar :: Int 62 | } 63 | 64 | foreign import test :: 65 | Foo 66 | { bar :: Int 67 | } 68 | 69 | foreign import test :: 70 | Foo 71 | { bar :: Int 72 | } -> 73 | Foo 74 | { bar :: Int 75 | } 76 | 77 | foreign import test :: 78 | Foo 79 | { bar :: Int 80 | } -> 81 | Foo 82 | { bar :: Int 83 | } 84 | 85 | foreign import test :: 86 | { foo :: Int 87 | , bar :: Int 88 | } 89 | -------------------------------------------------------------------------------- /test/snapshots/Guards.input: -------------------------------------------------------------------------------- 1 | module Guards where 2 | 3 | test 4 | | a = b 5 | | otherwise = c 6 | 7 | test | a = b 8 | | otherwise = c 9 | 10 | test | a = b | otherwise = c 11 | 12 | test | a = 13 | b 14 | | otherwise = c 15 | 16 | test | a = 17 | b | otherwise = 18 | c 19 | 20 | test = case _ of 21 | a 22 | | a -> b 23 | | otherwise -> c 24 | 25 | test = case _ of 26 | a | a -> b 27 | | otherwise -> c 28 | 29 | test = case _ of 30 | a | a -> b | otherwise -> c 31 | 32 | test = case _ of 33 | a | a -> 34 | b | otherwise -> 35 | c 36 | 37 | test = case _ of 38 | a | a -> 39 | b 40 | | otherwise -> 41 | c 42 | -------------------------------------------------------------------------------- /test/snapshots/Guards.output: -------------------------------------------------------------------------------- 1 | module Guards where 2 | 3 | test 4 | | a = b 5 | | otherwise = c 6 | 7 | test 8 | | a = b 9 | | otherwise = c 10 | 11 | test 12 | | a = b 13 | | otherwise = c 14 | 15 | test 16 | | a = 17 | b 18 | | otherwise = c 19 | 20 | test 21 | | a = 22 | b 23 | | otherwise = 24 | c 25 | 26 | test = case _ of 27 | a 28 | | a -> b 29 | | otherwise -> c 30 | 31 | test = case _ of 32 | a 33 | | a -> b 34 | | otherwise -> c 35 | 36 | test = case _ of 37 | a 38 | | a -> b 39 | | otherwise -> c 40 | 41 | test = case _ of 42 | a 43 | | a -> 44 | b 45 | | otherwise -> 46 | c 47 | 48 | test = case _ of 49 | a 50 | | a -> 51 | b 52 | | otherwise -> 53 | c 54 | -------------------------------------------------------------------------------- /test/snapshots/HTML.input: -------------------------------------------------------------------------------- 1 | module HTML where 2 | 3 | import Halogen.HTML as HH 4 | import Halogen.HTML.Events as HE 5 | 6 | -- Adapted from: 7 | -- https://github.com/purescript-halogen/purescript-halogen/blob/master/examples/higher-order-components/src/Harness.purs 8 | render state = 9 | HH.div_ 10 | [ HH.div_ [ HH.button 11 | [ HE.onClick \_ -> CheckButtonState ] 12 | [ HH.text "Check button state" ] 13 | , HH.p_ 14 | [ 15 | 16 | HH.text ("Last result: " <> printButtonState state.buttonCheckState)] 17 | ] 18 | , HH.div_ 19 | [ HH.p_ 20 | [ HH.text ("Last message from the button: " <> printButtonState state.buttonMessageState 21 | ) 22 | ] 23 | ] 24 | ] 25 | -------------------------------------------------------------------------------- /test/snapshots/HTML.output: -------------------------------------------------------------------------------- 1 | module HTML where 2 | 3 | import Halogen.HTML as HH 4 | import Halogen.HTML.Events as HE 5 | 6 | -- Adapted from: 7 | -- https://github.com/purescript-halogen/purescript-halogen/blob/master/examples/higher-order-components/src/Harness.purs 8 | render state = 9 | HH.div_ 10 | [ HH.div_ 11 | [ HH.button 12 | [ HE.onClick \_ -> CheckButtonState ] 13 | [ HH.text "Check button state" ] 14 | , HH.p_ 15 | [ HH.text ("Last result: " <> printButtonState state.buttonCheckState) 16 | ] 17 | ] 18 | , HH.div_ 19 | [ HH.p_ 20 | [ HH.text 21 | ( "Last message from the button: " <> printButtonState state.buttonMessageState 22 | ) 23 | ] 24 | ] 25 | ] 26 | -------------------------------------------------------------------------------- /test/snapshots/IfThenElse.input: -------------------------------------------------------------------------------- 1 | module IfThenElse where 2 | 3 | test = if a then b else c 4 | 5 | test = if a then 6 | b else c 7 | 8 | test = if a 9 | then b 10 | else c 11 | 12 | test = 13 | if a 14 | then b 15 | else c 16 | 17 | test = if a then b else if x then c else d 18 | 19 | test = if a then 20 | b else if x then c else d 21 | 22 | test = if a 23 | then b 24 | else if x 25 | then c 26 | else d 27 | 28 | test = 29 | if a then b 30 | else if x then c 31 | else d 32 | 33 | test = 34 | if a then 35 | b 36 | else if x then 37 | c 38 | else 39 | d 40 | 41 | test = 42 | if a then do 43 | b 44 | else do 45 | c 46 | -------------------------------------------------------------------------------- /test/snapshots/IfThenElse.output: -------------------------------------------------------------------------------- 1 | module IfThenElse where 2 | 3 | test = if a then b else c 4 | 5 | test = 6 | if a then 7 | b 8 | else c 9 | 10 | test = 11 | if a then b 12 | else c 13 | 14 | test = 15 | if a then b 16 | else c 17 | 18 | test = if a then b else if x then c else d 19 | 20 | test = 21 | if a then 22 | b 23 | else if x then c 24 | else d 25 | 26 | test = 27 | if a then b 28 | else if x then c 29 | else d 30 | 31 | test = 32 | if a then b 33 | else if x then c 34 | else d 35 | 36 | test = 37 | if a then 38 | b 39 | else if x then 40 | c 41 | else 42 | d 43 | 44 | test = 45 | if a then do 46 | b 47 | else do 48 | c 49 | -------------------------------------------------------------------------------- /test/snapshots/ImportSortIde.input: -------------------------------------------------------------------------------- 1 | -- @format --import-sort-ide 2 | module ImportSortIde where 3 | 4 | import Prim hiding (Type, Row) 5 | 6 | import Data.Array (sortBy) as Z 7 | import Data.Array (sortBy) as Array 8 | import Data.Array (sortBy) as A 9 | import Data.Array (sortBy) 10 | 11 | import Data.Array hiding (sortBy) as Z 12 | import Data.Array hiding (sortBy) as Array 13 | import Data.Array hiding (sortBy) as A 14 | 15 | import Data.Array as Z 16 | import Data.Array as Array 17 | import Data.Array as A 18 | 19 | import Example (Test, type (+), Test(BBB, AAA), Test(..), Test(), test, class Test) 20 | import Example (class Test) as Exports 21 | 22 | import Prelude 23 | -------------------------------------------------------------------------------- /test/snapshots/ImportSortIde.output: -------------------------------------------------------------------------------- 1 | module ImportSortIde where 2 | 3 | import Prim hiding (Type, Row) 4 | 5 | import Data.Array (sortBy) as Z 6 | import Data.Array (sortBy) as Array 7 | import Data.Array (sortBy) as A 8 | import Data.Array (sortBy) 9 | 10 | import Data.Array hiding (sortBy) as Z 11 | import Data.Array hiding (sortBy) as Array 12 | import Data.Array hiding (sortBy) as A 13 | 14 | import Data.Array as Z 15 | import Data.Array as Array 16 | import Data.Array as A 17 | 18 | import Example (Test, type (+), Test(BBB, AAA), Test(..), Test(), test, class Test) 19 | import Example (class Test) as Exports 20 | 21 | import Prelude 22 | 23 | -- @format --import-sort-ide 24 | module ImportSortIde where 25 | 26 | import Prelude 27 | import Prim hiding (Row, Type) 28 | 29 | import Data.Array (sortBy) 30 | import Data.Array (sortBy) as A 31 | import Data.Array (sortBy) as Array 32 | import Data.Array (sortBy) as Z 33 | import Data.Array as A 34 | import Data.Array as Array 35 | import Data.Array as Z 36 | import Data.Array hiding (sortBy) as A 37 | import Data.Array hiding (sortBy) as Array 38 | import Data.Array hiding (sortBy) as Z 39 | import Example (class Test) as Exports 40 | import Example (class Test, type (+), Test(..), Test, Test(), Test(BBB, AAA), test) 41 | -------------------------------------------------------------------------------- /test/snapshots/ImportWrap.input: -------------------------------------------------------------------------------- 1 | -- @format --import-wrap-source --width 20 2 | -- @format --import-wrap-auto --width 20 3 | module ImportWrap where 4 | 5 | import Test (Test, Test, Test, Test, Test, Test, Test, Test, Test) 6 | import Test (Test, Test, 7 | Test, Test, Test, Test, Test, Test, Test) 8 | -------------------------------------------------------------------------------- /test/snapshots/ImportWrap.output: -------------------------------------------------------------------------------- 1 | module ImportWrap where 2 | 3 | import Test (Test, Test, Test, Test, Test, Test, Test, Test, Test) 4 | import Test 5 | ( Test 6 | , Test 7 | , Test 8 | , Test 9 | , Test 10 | , Test 11 | , Test 12 | , Test 13 | , Test 14 | ) 15 | 16 | -- @format --import-wrap-auto --width 20 17 | module ImportWrap where 18 | 19 | import Test 20 | ( Test 21 | , Test 22 | , Test 23 | , Test 24 | , Test 25 | , Test 26 | , Test 27 | , Test 28 | , Test 29 | ) 30 | import Test 31 | ( Test 32 | , Test 33 | , Test 34 | , Test 35 | , Test 36 | , Test 37 | , Test 38 | , Test 39 | , Test 40 | ) 41 | 42 | -- @format --import-wrap-source --width 20 43 | module ImportWrap where 44 | 45 | import Test (Test, Test, Test, Test, Test, Test, Test, Test, Test) 46 | import Test 47 | ( Test 48 | , Test 49 | , Test 50 | , Test 51 | , Test 52 | , Test 53 | , Test 54 | , Test 55 | , Test 56 | ) 57 | -------------------------------------------------------------------------------- /test/snapshots/Imports.input: -------------------------------------------------------------------------------- 1 | module Foo where 2 | 3 | import Prelude 4 | 5 | 6 | import Conduit.Prelude 7 | 8 | import Effect (log) 9 | import Data.Functor (class Functor, map, void) 10 | import Data.Either (Either()) 11 | 12 | import Data.List hiding (cons) 13 | import Data.Maybe (Maybe (Just, Nothing), maybe) 14 | import Prelude 15 | ( class Semiring 16 | , Ordering (EQ, GT, LT) 17 | , Void 18 | , compose 19 | , one 20 | , (&&) 21 | , (<<<) 22 | , (~>) 23 | ) 24 | 25 | import Data.Map (singleton 26 | , empty) as Data.Map 27 | import Data.List as List 28 | import Data.Maybe 29 | as Maybe 30 | -------------------------------------------------------------------------------- /test/snapshots/Imports.output: -------------------------------------------------------------------------------- 1 | module Foo where 2 | 3 | import Prelude 4 | 5 | import Conduit.Prelude 6 | 7 | import Effect (log) 8 | import Data.Functor (class Functor, map, void) 9 | import Data.Either (Either()) 10 | 11 | import Data.List hiding (cons) 12 | import Data.Maybe (Maybe(Just, Nothing), maybe) 13 | import Prelude 14 | ( class Semiring 15 | , Ordering(EQ, GT, LT) 16 | , Void 17 | , compose 18 | , one 19 | , (&&) 20 | , (<<<) 21 | , (~>) 22 | ) 23 | 24 | import Data.Map 25 | ( singleton 26 | , empty 27 | ) as Data.Map 28 | import Data.List as List 29 | import Data.Maybe as Maybe 30 | -------------------------------------------------------------------------------- /test/snapshots/Instance.input: -------------------------------------------------------------------------------- 1 | -- @format --arrow-last 2 | module Instance where 3 | 4 | data Foo k 5 | 6 | instance semigroupFoo :: (Semigroup k) 7 | => Semigroup (Foo k) 8 | where 9 | append = 10 | foo 11 | 12 | instance 13 | (Monoid k) 14 | => Monoid (Foo k) where 15 | append x y = foo x 16 | y 17 | -------------------------------------------------------------------------------- /test/snapshots/Instance.output: -------------------------------------------------------------------------------- 1 | module Instance where 2 | 3 | data Foo k 4 | 5 | instance semigroupFoo :: 6 | ( Semigroup k 7 | ) => 8 | Semigroup (Foo k) 9 | where 10 | append = 11 | foo 12 | 13 | instance 14 | ( Monoid k 15 | ) => 16 | Monoid (Foo k) where 17 | append x y = foo x 18 | y 19 | 20 | -- @format --arrow-last 21 | module Instance where 22 | 23 | data Foo k 24 | 25 | instance semigroupFoo :: 26 | ( Semigroup k 27 | ) => 28 | Semigroup (Foo k) 29 | where 30 | append = 31 | foo 32 | 33 | instance 34 | ( Monoid k 35 | ) => 36 | Monoid (Foo k) where 37 | append x y = foo x 38 | y 39 | -------------------------------------------------------------------------------- /test/snapshots/InstanceChain.input: -------------------------------------------------------------------------------- 1 | -- @format --arrow-last 2 | module InstanceChain where 3 | 4 | data Foo k 5 | 6 | instance semigroupFooBlah :: (Semigroup k) 7 | => Semigroup (Foo k) where 8 | append = foo 9 | else instance semigroupFooBlaz :: Semigroup (Foo k) where 10 | append x y = x 11 | -------------------------------------------------------------------------------- /test/snapshots/InstanceChain.output: -------------------------------------------------------------------------------- 1 | module InstanceChain where 2 | 3 | data Foo k 4 | 5 | instance semigroupFooBlah :: 6 | ( Semigroup k 7 | ) => 8 | Semigroup (Foo k) where 9 | append = foo 10 | else instance semigroupFooBlaz :: Semigroup (Foo k) where 11 | append x y = x 12 | 13 | -- @format --arrow-last 14 | module InstanceChain where 15 | 16 | data Foo k 17 | 18 | instance semigroupFooBlah :: 19 | ( Semigroup k 20 | ) => 21 | Semigroup (Foo k) where 22 | append = foo 23 | else instance semigroupFooBlaz :: Semigroup (Foo k) where 24 | append x y = x 25 | -------------------------------------------------------------------------------- /test/snapshots/ModuleHeader.input: -------------------------------------------------------------------------------- 1 | module 2 | Foo.Bar where 3 | -------------------------------------------------------------------------------- /test/snapshots/ModuleHeader.output: -------------------------------------------------------------------------------- 1 | module Foo.Bar where 2 | -------------------------------------------------------------------------------- /test/snapshots/MultiCase.input: -------------------------------------------------------------------------------- 1 | module MultiCase where 2 | 3 | test = case _, _ of 4 | Foo a, Bar b -> 5 | a <> b 6 | 7 | test = case _, _ of 8 | Foo a, Bar { b 9 | , c 10 | } -> 11 | a <> b <> c 12 | 13 | test = case _, _ of 14 | -- Comment 15 | Foo a, Bar b -> 16 | a <> b 17 | -------------------------------------------------------------------------------- /test/snapshots/MultiCase.output: -------------------------------------------------------------------------------- 1 | module MultiCase where 2 | 3 | test = case _, _ of 4 | Foo a, Bar b -> 5 | a <> b 6 | 7 | test = case _, _ of 8 | Foo a, 9 | Bar 10 | { b 11 | , c 12 | } -> 13 | a <> b <> c 14 | 15 | test = case _, _ of 16 | -- Comment 17 | Foo a, Bar b -> 18 | a <> b 19 | -------------------------------------------------------------------------------- /test/snapshots/MultilineApplications.input: -------------------------------------------------------------------------------- 1 | module MultilineApplications where 2 | 3 | import Prelude 4 | 5 | test = 6 | do 7 | foo 8 | bar 9 | 10 | test = do 11 | foo 12 | bar 13 | 14 | test = 15 | foo 16 | # do 17 | foo 18 | bar 19 | 20 | test = 21 | foo 22 | # do 23 | foo 24 | bar 25 | # do 26 | foo 27 | bar 28 | 29 | test = 30 | do 31 | foo 32 | >>> 33 | bar 34 | 35 | test = do 36 | foo 37 | >>> 38 | bar 39 | 40 | test = 41 | foo 42 | # do 43 | foo 44 | >>> 45 | bar 46 | 47 | test = 48 | case foo of 49 | Bar -> 42 50 | >>> 51 | bar 52 | 53 | test = case foo of 54 | Bar -> 42 55 | >>> 56 | bar 57 | 58 | test = 59 | case foo of 60 | Bar -> 42 61 | bar 62 | 63 | test = case foo of 64 | Bar -> 42 65 | bar 66 | 67 | 68 | test = 69 | foo bar 70 | # foo 71 | 72 | test = 73 | foo 74 | bar do 75 | baz do 76 | 42 77 | 78 | test = 79 | foo 80 | bar do 81 | baz do 82 | 42 83 | qux 84 | 85 | test = 86 | foo 87 | bar do 88 | baz 89 | bar do 90 | baz 91 | bar do 92 | baz 93 | 94 | test = foo bar \_ -> baz case _ of 95 | Foo -> 42 96 | 97 | test = foo bar 98 | \_ -> baz case _ of 99 | Foo -> 42 100 | 101 | test = foo 102 | bar 103 | \_ -> baz case _ of 104 | Foo -> 42 105 | 106 | test = 107 | foo 108 | bar 109 | \_ -> baz case _ of 110 | Foo -> 42 111 | -------------------------------------------------------------------------------- /test/snapshots/MultilineApplications.output: -------------------------------------------------------------------------------- 1 | module MultilineApplications where 2 | 3 | import Prelude 4 | 5 | test = 6 | do 7 | foo 8 | bar 9 | 10 | test = 11 | do 12 | foo 13 | bar 14 | 15 | test = 16 | foo 17 | # 18 | do 19 | foo 20 | bar 21 | 22 | test = 23 | foo 24 | # 25 | do 26 | foo 27 | bar 28 | # 29 | do 30 | foo 31 | bar 32 | 33 | test = 34 | do 35 | foo 36 | >>> 37 | bar 38 | 39 | test = 40 | do 41 | foo 42 | >>> 43 | bar 44 | 45 | test = 46 | foo 47 | # do 48 | foo 49 | >>> 50 | bar 51 | 52 | test = 53 | case foo of 54 | Bar -> 42 55 | >>> 56 | bar 57 | 58 | test = 59 | case foo of 60 | Bar -> 42 61 | >>> 62 | bar 63 | 64 | test = 65 | case foo of 66 | Bar -> 42 67 | bar 68 | 69 | test = 70 | case foo of 71 | Bar -> 42 72 | bar 73 | 74 | test = 75 | foo bar 76 | # foo 77 | 78 | test = 79 | foo 80 | bar 81 | do 82 | baz do 83 | 42 84 | 85 | test = 86 | foo 87 | bar 88 | do 89 | baz do 90 | 42 91 | qux 92 | 93 | test = 94 | foo 95 | bar 96 | do 97 | baz 98 | bar 99 | do 100 | baz 101 | bar 102 | do 103 | baz 104 | 105 | test = foo bar \_ -> baz case _ of 106 | Foo -> 42 107 | 108 | test = foo bar 109 | \_ -> baz case _ of 110 | Foo -> 42 111 | 112 | test = foo 113 | bar 114 | \_ -> baz case _ of 115 | Foo -> 42 116 | 117 | test = 118 | foo 119 | bar 120 | \_ -> baz case _ of 121 | Foo -> 42 122 | -------------------------------------------------------------------------------- /test/snapshots/MultilineBindings.input: -------------------------------------------------------------------------------- 1 | module MultilineBindings where 2 | 3 | test = ok 4 | [ a 5 | , b 6 | , c 7 | ] 8 | 9 | test = do 10 | let test = ok 11 | [ a 12 | , b 13 | , c 14 | ] 15 | test 16 | 17 | test = ado 18 | let test = ok 19 | [ a 20 | , b 21 | , c 22 | ] 23 | in test 24 | 25 | test = do 26 | test <- ok 27 | [ a 28 | , b 29 | , c 30 | ] 31 | test 32 | 33 | test = ado 34 | test <- ok 35 | [ a 36 | , b 37 | , c 38 | ] 39 | in test 40 | 41 | test = case _ of 42 | Test -> ok 43 | [ a 44 | , b 45 | , c 46 | ] 47 | 48 | test = case _ of 49 | Test 50 | | true -> ok 51 | [ a 52 | , b 53 | , c 54 | ] 55 | -------------------------------------------------------------------------------- /test/snapshots/MultilineBindings.output: -------------------------------------------------------------------------------- 1 | module MultilineBindings where 2 | 3 | test = ok 4 | [ a 5 | , b 6 | , c 7 | ] 8 | 9 | test = do 10 | let 11 | test = ok 12 | [ a 13 | , b 14 | , c 15 | ] 16 | test 17 | 18 | test = ado 19 | let 20 | test = ok 21 | [ a 22 | , b 23 | , c 24 | ] 25 | in test 26 | 27 | test = do 28 | test <- ok 29 | [ a 30 | , b 31 | , c 32 | ] 33 | test 34 | 35 | test = ado 36 | test <- ok 37 | [ a 38 | , b 39 | , c 40 | ] 41 | in test 42 | 43 | test = case _ of 44 | Test -> ok 45 | [ a 46 | , b 47 | , c 48 | ] 49 | 50 | test = case _ of 51 | Test 52 | | true -> ok 53 | [ a 54 | , b 55 | , c 56 | ] 57 | -------------------------------------------------------------------------------- /test/snapshots/MultilineNamedBinders.input: -------------------------------------------------------------------------------- 1 | module MultilineNamedBinders where 2 | 3 | test ok@{ a , b , c 4 | } = 5 | a b c 6 | 7 | test = do 8 | ok@{ a , b , c 9 | } <- foo 10 | a b c 11 | 12 | test = ado 13 | ok@{ a , b , c 14 | } <- foo 15 | in a b c 16 | 17 | test = do 18 | let ok@{ a , b , c 19 | } = a b c 20 | a b c 21 | 22 | test = ado 23 | let ok@{ a , b , c 24 | } = a b c 25 | in a b c 26 | 27 | test = 28 | let ok@{ a , b , c 29 | } = a b c 30 | in a b c 31 | 32 | test = a b c 33 | where 34 | ok@{ a , b , c 35 | } = a b c 36 | 37 | test = case _ of 38 | ok@{ a , b , c 39 | } -> a b c 40 | -------------------------------------------------------------------------------- /test/snapshots/MultilineNamedBinders.output: -------------------------------------------------------------------------------- 1 | module MultilineNamedBinders where 2 | 3 | test 4 | ok@ 5 | { a 6 | , b 7 | , c 8 | } = 9 | a b c 10 | 11 | test = do 12 | ok@ 13 | { a 14 | , b 15 | , c 16 | } <- foo 17 | a b c 18 | 19 | test = ado 20 | ok@ 21 | { a 22 | , b 23 | , c 24 | } <- foo 25 | in a b c 26 | 27 | test = do 28 | let 29 | ok@ 30 | { a 31 | , b 32 | , c 33 | } = a b c 34 | a b c 35 | 36 | test = ado 37 | let 38 | ok@ 39 | { a 40 | , b 41 | , c 42 | } = a b c 43 | in a b c 44 | 45 | test = 46 | let 47 | ok@ 48 | { a 49 | , b 50 | , c 51 | } = a b c 52 | in 53 | a b c 54 | 55 | test = a b c 56 | where 57 | ok@ 58 | { a 59 | , b 60 | , c 61 | } = a b c 62 | 63 | test = case _ of 64 | ok@ 65 | { a 66 | , b 67 | , c 68 | } -> a b c 69 | -------------------------------------------------------------------------------- /test/snapshots/MultilineOperatorArguments.input: -------------------------------------------------------------------------------- 1 | module MultilineOperatorArguments where 2 | 3 | test = foo # 4 | case _ of 5 | Foo -> 42 6 | # map (\a -> 7 | a) 8 | # map (\a -> 9 | a) 10 | 11 | test = foo # 12 | case _ of 13 | Foo -> 42 14 | # map (\a -> 15 | a) 16 | # 17 | map (\a -> 18 | a) 19 | 20 | test = foo # 21 | case _ of 22 | Foo -> 42 23 | # map (\a -> 24 | a) 25 | >>> map (\a -> 26 | a) 27 | 28 | test = foo # 29 | case _ of 30 | Foo -> 42 31 | # map (\a -> 32 | a) 33 | >>> 34 | map (\a -> 35 | a) 36 | 37 | test = 38 | foo >>> case _ of 39 | Foo -> 42 40 | >>> case _ of 41 | Foo -> 42 42 | # case _ of 43 | Foo -> 42 44 | 45 | test = foo # map (\a -> a) # case _ of 46 | Foo -> 42 47 | -------------------------------------------------------------------------------- /test/snapshots/MultilineOperatorArguments.output: -------------------------------------------------------------------------------- 1 | module MultilineOperatorArguments where 2 | 3 | test = foo 4 | # case _ of 5 | Foo -> 42 6 | # map 7 | ( \a -> 8 | a 9 | ) 10 | # map 11 | ( \a -> 12 | a 13 | ) 14 | 15 | test = foo 16 | # case _ of 17 | Foo -> 42 18 | # map 19 | ( \a -> 20 | a 21 | ) 22 | # 23 | map 24 | ( \a -> 25 | a 26 | ) 27 | 28 | test = foo 29 | # case _ of 30 | Foo -> 42 31 | # map 32 | ( \a -> 33 | a 34 | ) 35 | >>> map 36 | ( \a -> 37 | a 38 | ) 39 | 40 | test = foo 41 | # case _ of 42 | Foo -> 42 43 | # map 44 | ( \a -> 45 | a 46 | ) 47 | >>> 48 | map 49 | ( \a -> 50 | a 51 | ) 52 | 53 | test = 54 | foo 55 | >>> case _ of 56 | Foo -> 42 57 | >>> case _ of 58 | Foo -> 42 59 | # case _ of 60 | Foo -> 42 61 | 62 | test = foo # map (\a -> a) # case _ of 63 | Foo -> 42 64 | -------------------------------------------------------------------------------- /test/snapshots/MultilineStringLiterals.input: -------------------------------------------------------------------------------- 1 | module MultilineStringLiterals where 2 | 3 | test = """ """ 4 | 5 | test = """ 6 | this is a string 7 | """ 8 | 9 | test = """ 10 | this is a string 11 | 12 | this is a string 13 | 14 | 15 | 16 | this is a string 17 | """ 18 | 19 | test = 20 | foo """ 21 | this is a string 22 | """ 23 | 24 | test = 25 | foo 26 | """ 27 | this is a string 28 | """ 29 | 30 | test = 31 | foo 32 | """ ok 33 | this is a string 34 | """ 35 | 36 | test = 37 | foo """ ok 38 | this is a string 39 | """ bar 40 | 41 | test = 42 | foo " ok\ 43 | \ this is a string\ 44 | \" bar 45 | 46 | test = 47 | foo " ok\ 48 | \ this is a string\ 49 | \ this is a string\ 50 | \ this is a string\ 51 | \" bar 52 | -------------------------------------------------------------------------------- /test/snapshots/MultilineStringLiterals.output: -------------------------------------------------------------------------------- 1 | module MultilineStringLiterals where 2 | 3 | test = """ """ 4 | 5 | test = 6 | """ 7 | this is a string 8 | """ 9 | 10 | test = 11 | """ 12 | this is a string 13 | 14 | this is a string 15 | 16 | 17 | 18 | this is a string 19 | """ 20 | 21 | test = 22 | foo 23 | """ 24 | this is a string 25 | """ 26 | 27 | test = 28 | foo 29 | """ 30 | this is a string 31 | """ 32 | 33 | test = 34 | foo 35 | """ ok 36 | this is a string 37 | """ 38 | 39 | test = 40 | foo 41 | """ ok 42 | this is a string 43 | """ 44 | bar 45 | 46 | test = 47 | foo 48 | " ok\ 49 | \ this is a string\ 50 | \" 51 | bar 52 | 53 | test = 54 | foo 55 | " ok\ 56 | \ this is a string\ 57 | \ this is a string\ 58 | \ this is a string\ 59 | \" 60 | bar 61 | -------------------------------------------------------------------------------- /test/snapshots/MultilineSuperClasses.input: -------------------------------------------------------------------------------- 1 | module MultilineSuperClasses where 2 | 3 | class (Foo m 4 | , Bar m) <= Baz m 5 | 6 | class 7 | ( Foo m 8 | , Bar m 9 | ) <= 10 | Baz m 11 | 12 | class (Foo m 13 | , Bar m) <= Baz m where 14 | foo :: m 15 | 16 | class 17 | ( Foo m 18 | , Bar m 19 | ) <= 20 | Baz m 21 | where 22 | foo :: m 23 | 24 | instance (Foo m 25 | , Bar m) => Baz m where 26 | foo = undefined 27 | 28 | instance 29 | ( Foo m 30 | , Bar m 31 | ) => 32 | Baz m where 33 | foo = undefined 34 | -------------------------------------------------------------------------------- /test/snapshots/MultilineSuperClasses.output: -------------------------------------------------------------------------------- 1 | module MultilineSuperClasses where 2 | 3 | class 4 | ( Foo m 5 | , Bar m 6 | ) <= 7 | Baz m 8 | 9 | class 10 | ( Foo m 11 | , Bar m 12 | ) <= 13 | Baz m 14 | 15 | class 16 | ( Foo m 17 | , Bar m 18 | ) <= 19 | Baz m where 20 | foo :: m 21 | 22 | class 23 | ( Foo m 24 | , Bar m 25 | ) <= 26 | Baz m 27 | where 28 | foo :: m 29 | 30 | instance 31 | ( Foo m 32 | , Bar m 33 | ) => 34 | Baz m where 35 | foo = undefined 36 | 37 | instance 38 | ( Foo m 39 | , Bar m 40 | ) => 41 | Baz m where 42 | foo = undefined 43 | -------------------------------------------------------------------------------- /test/snapshots/OperatorsReversed.input: -------------------------------------------------------------------------------- 1 | module OperatorsReversed where 2 | 3 | -- The last operand is allowed to break and indent without effecting 4 | -- the rest of the operator chain. 5 | test = 6 | a <> b <> c <> 7 | d 8 | 9 | -- A break within the operator chain effects the rest of the chain. 10 | test = 11 | a 12 | <> b <> c <> 13 | d 14 | 15 | -- Operator last needs to be flipped back to operator first. 16 | test = 17 | a <> 18 | b <> 19 | c <> 20 | d 21 | 22 | -- Multiline delimiters need to retain a break after the operator. 23 | test = 24 | a <> 25 | [ 1 26 | , 2, 3 ] <> 27 | [ 1 28 | , 2, 3 ] 29 | 30 | -- Multiline hanging elements need to retain their multiline state. 31 | test = 32 | a # 33 | ( \x -> 34 | x) # 35 | ( \x -> 36 | x) 37 | 38 | 39 | -- For operator first, a break should be inserted after the operator. 40 | -- Testing together because the rule for collapsing the break affects this. 41 | test = 42 | a 43 | <> [ 1 44 | , 2, 3 ] 45 | <> [ 1 46 | , 2, 3 ] 47 | 48 | test = 49 | a 50 | # ( \x -> 51 | x) 52 | # ( \x -> 53 | x) 54 | -------------------------------------------------------------------------------- /test/snapshots/OperatorsReversed.output: -------------------------------------------------------------------------------- 1 | module OperatorsReversed where 2 | 3 | -- The last operand is allowed to break and indent without effecting 4 | -- the rest of the operator chain. 5 | test = 6 | a <> b <> c <> 7 | d 8 | 9 | -- A break within the operator chain effects the rest of the chain. 10 | test = 11 | a 12 | <> b 13 | <> c 14 | <> 15 | d 16 | 17 | -- Operator last needs to be flipped back to operator first. 18 | test = 19 | a 20 | <> b 21 | <> c 22 | <> 23 | d 24 | 25 | -- Multiline delimiters need to retain a break after the operator. 26 | test = 27 | a 28 | <> 29 | [ 1 30 | , 2 31 | , 3 32 | ] 33 | <> 34 | [ 1 35 | , 2 36 | , 3 37 | ] 38 | 39 | -- Multiline hanging elements need to retain their multiline state. 40 | test = 41 | a 42 | # 43 | ( \x -> 44 | x 45 | ) 46 | # 47 | ( \x -> 48 | x 49 | ) 50 | 51 | -- For operator first, a break should be inserted after the operator. 52 | -- Testing together because the rule for collapsing the break affects this. 53 | test = 54 | a 55 | <> 56 | [ 1 57 | , 2 58 | , 3 59 | ] 60 | <> 61 | [ 1 62 | , 2 63 | , 3 64 | ] 65 | 66 | test = 67 | a 68 | # 69 | ( \x -> 70 | x 71 | ) 72 | # 73 | ( \x -> 74 | x 75 | ) 76 | -------------------------------------------------------------------------------- /test/snapshots/Shebang.input: -------------------------------------------------------------------------------- 1 | #! shebang 1 2 | #! shebang 2 3 | -- comment 4 | module Foo.Bar where 5 | -------------------------------------------------------------------------------- /test/snapshots/Shebang.output: -------------------------------------------------------------------------------- 1 | #! shebang 1 2 | #! shebang 2 3 | -- comment 4 | module Foo.Bar where 5 | -------------------------------------------------------------------------------- /test/snapshots/TrailingLineComments.input: -------------------------------------------------------------------------------- 1 | -- @format --width=10 2 | module TrailingLineComments where 3 | 4 | test -- ok 5 | = test 6 | 7 | test = test -- ok 8 | 9 | test = test {- a -} {- b -} -- ok 10 | -------------------------------------------------------------------------------- /test/snapshots/TrailingLineComments.output: -------------------------------------------------------------------------------- 1 | module TrailingLineComments where 2 | 3 | test -- ok 4 | = test 5 | 6 | test = test -- ok 7 | 8 | test = test {- a -} {- b -} -- ok 9 | 10 | -- @format --width=10 11 | module TrailingLineComments where 12 | 13 | test -- ok 14 | = test 15 | 16 | test = 17 | test -- ok 18 | 19 | test = 20 | test {- a -} {- b -} -- ok 21 | -------------------------------------------------------------------------------- /test/snapshots/UnicodeSignatures.input: -------------------------------------------------------------------------------- 1 | -- @format --unicode-always 2 | -- @format --unicode-never 3 | module UnicodeSignatures where 4 | 5 | test 6 | ∷ ∀ a 7 | . a 8 | → a 9 | -------------------------------------------------------------------------------- /test/snapshots/UnicodeSignatures.output: -------------------------------------------------------------------------------- 1 | module UnicodeSignatures where 2 | 3 | test 4 | ∷ ∀ a 5 | . a 6 | → a 7 | 8 | -- @format --unicode-always 9 | module UnicodeSignatures where 10 | 11 | test 12 | ∷ ∀ a 13 | . a 14 | → a 15 | 16 | -- @format --unicode-never 17 | module UnicodeSignatures where 18 | 19 | test 20 | :: forall a 21 | . a 22 | -> a 23 | -------------------------------------------------------------------------------- /test/snapshots/UnicodeSuperclass.input: -------------------------------------------------------------------------------- 1 | -- @format --unicode-always 2 | -- @format --unicode-never 3 | module UnicodeSuperclass where 4 | 5 | class Foo a <= Bar a 6 | class Foo a ⇐ Bar a 7 | -------------------------------------------------------------------------------- /test/snapshots/UnicodeSuperclass.output: -------------------------------------------------------------------------------- 1 | module UnicodeSuperclass where 2 | 3 | class Foo a <= Bar a 4 | class Foo a ⇐ Bar a 5 | 6 | -- @format --unicode-always 7 | module UnicodeSuperclass where 8 | 9 | class Foo a ⇐ Bar a 10 | class Foo a ⇐ Bar a 11 | 12 | -- @format --unicode-never 13 | module UnicodeSuperclass where 14 | 15 | class Foo a <= Bar a 16 | class Foo a <= Bar a 17 | -------------------------------------------------------------------------------- /test/snapshots/UnusualLineComments.input: -------------------------------------------------------------------------------- 1 | module UnusualLineComments where 2 | 3 | test = do 4 | test 5 | -- test 6 | <- ok 7 | [ a 8 | , b 9 | , c 10 | ] 11 | test 12 | 13 | test = do 14 | let { test } 15 | -- test 16 | = ok 17 | [ a 18 | , b 19 | , c 20 | ] 21 | test 22 | -------------------------------------------------------------------------------- /test/snapshots/UnusualLineComments.output: -------------------------------------------------------------------------------- 1 | module UnusualLineComments where 2 | 3 | test = do 4 | test 5 | -- test 6 | <- ok 7 | [ a 8 | , b 9 | , c 10 | ] 11 | test 12 | 13 | test = do 14 | let 15 | { test } 16 | -- test 17 | = ok 18 | [ a 19 | , b 20 | , c 21 | ] 22 | test 23 | -------------------------------------------------------------------------------- /test/snapshots/VisibleTypeApplications.input: -------------------------------------------------------------------------------- 1 | module VisibleTypeApplications where 2 | 3 | test :: forall @a. a -> a 4 | 5 | test :: forall @ 6 | -- wat 7 | a. a -> a 8 | 9 | test :: forall @{- wat -}a. a -> a 10 | 11 | test :: forall (@a :: Type). a -> a 12 | 13 | test :: forall (@ 14 | -- wat 15 | a :: Type). a -> a 16 | 17 | test :: forall (@{- wat -}a :: Type). a -> a 18 | 19 | test = foo @Bar 42 20 | 21 | test = foo @Bar 22 | 42 23 | 24 | test = foo 25 | @Bar 26 | 42 27 | 28 | test = foo @( 29 | Bar 30 | { baz :: Int 31 | , quux :: String 32 | } 33 | ) 42 34 | 35 | test = foo @Bar 42 @( 36 | Baz 37 | { baz :: Int 38 | , quux :: String 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /test/snapshots/VisibleTypeApplications.output: -------------------------------------------------------------------------------- 1 | module VisibleTypeApplications where 2 | 3 | test :: forall @a. a -> a 4 | 5 | test 6 | :: forall 7 | @ 8 | -- wat 9 | a 10 | . a 11 | -> a 12 | 13 | test :: forall @ {- wat -}a. a -> a 14 | 15 | test :: forall (@a :: Type). a -> a 16 | 17 | test 18 | :: forall 19 | ( @ 20 | -- wat 21 | a :: Type 22 | ) 23 | . a 24 | -> a 25 | 26 | test :: forall (@ {- wat -}a :: Type). a -> a 27 | 28 | test = foo @Bar 42 29 | 30 | test = foo @Bar 31 | 42 32 | 33 | test = foo 34 | @Bar 35 | 42 36 | 37 | test = foo 38 | @( Bar 39 | { baz :: Int 40 | , quux :: String 41 | } 42 | ) 43 | 42 44 | 45 | test = foo @Bar 42 46 | @( Baz 47 | { baz :: Int 48 | , quux :: String 49 | } 50 | ) 51 | --------------------------------------------------------------------------------