├── .gitignore ├── LICENSE ├── README.md ├── RELEASES.md ├── elm.json ├── src ├── Elm │ ├── CodeGen.elm │ ├── Comments.elm │ ├── DSLParser.elm │ └── Pretty.elm ├── ImportsAndExposing.elm └── Util.elm ├── test ├── .gitignore ├── elm.json ├── package-lock.json ├── package.json └── src │ ├── elm │ ├── Editor.elm │ ├── ToFix.elm │ └── Top.elm │ └── index.js └── tests └── Tests.elm /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff/ 2 | node_modules/ 3 | test/src/elm.js 4 | .tool-versions 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 The Sett Ltd. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Contacts for Support** 2 | - @rupertlssmith on https://elmlang.slack.com 3 | - @rupert on https://discourse.elm-lang.org 4 | 5 | # elm-syntax-dsl 6 | 7 | Provides a DSL that makes it simpler to write code in Elm that generates Elm code; simpler 8 | than `stil4m/elm-syntax` on which this DSL is based. 9 | 10 | Provides a pretty printer that prints out the abstract syntax tree constructed using 11 | the DSL as Elm source code, in a way that aims to be compatible with `elm-format`. 12 | 13 | ## Simpler code generation as a DSL. 14 | 15 | `stil4m/elm-syntax` uses a lot of records to capture parameters, and also wraps parts of the 16 | AST in `Node`s that capture source code lines and rows corresponding to parts of the AST. 17 | This DSL inlines most of the records as function parameters. 18 | 19 | `stil4m/elm-syntax` is spread across around 17 modules for the complete syntax. This makes 20 | working with it a little awkward because there are a lot of import statements to write. This 21 | DSL condenses everything into a single exposed module, which helps to get started with 22 | code generation more easily. 23 | 24 | Any parts of the AST needing to be wrapped in nodes are automatically wrapped in nodes with 25 | zeroed out source code position information. As the purpose of this package is code generation 26 | of Elm from scratch, rather than the analysis or manipulation of existing Elm, it is ok to 27 | have the source code positional information zeroed out. 28 | 29 | ### Automatic insertion of parenthesis. 30 | 31 | Parenthesis are inserted into code automatically if they are required by the precedence 32 | rules of Elm syntax. Note that this is done at the `Expression` level, as expressions 33 | are built, rather than during pretty printing. 34 | 35 | ``` 36 | import Elm.CodeGen as CodeGen 37 | import Elm.Pretty 38 | import Pretty 39 | 40 | expr = 41 | CodeGen.apply 42 | [ CodeGen.val "map" 43 | , CodeGen.construct "C" [] 44 | , CodeGen.apply [ CodeGen.val "foo", CodeGen.val "arg" ] 45 | ] 46 | 47 | 48 | result = 49 | Elm.Pretty.prettyExpression expr |> Pretty.pretty 120 50 | ``` 51 | 52 | Will print the result: 53 | 54 | ``` 55 | map C (foo arg) 56 | ``` 57 | 58 | ## Pretty printing stable under `elm-format`. 59 | 60 | The pretty printer aims to be fully stable with respect to `elm-format` in the sense that 61 | running `elm-format` on the output should have no effect at all. The advantage of this is 62 | that if generated code moves to being edited by hand, there will not be a large white-space 63 | only diff created when `elm-format` is applied. 64 | 65 | ### How to get the results as a String. 66 | 67 | To print the `Doc` created by the `pretty` functions, `the-sett/elm-pretty-printer` 68 | is used: 69 | 70 | ``` 71 | import Elm.Pretty 72 | import Pretty 73 | 74 | -- Fit to a page width of 120 characters 75 | elmAsString = 76 | Elm.Pretty.prepareLayout 120 someFile 77 | |> Pretty.pretty 120 78 | ``` 79 | 80 | ### Broken stuff in elm-syntax: 81 | 82 | * Not all Elm files I tried seem to parse. 83 | * Multi-line strings do something very weird and get jumbled up in order. 84 | * End-line comments are deleted. 85 | 86 | ### Unimplemented stuff: 87 | 88 | * GLSL syntax. 89 | * Any reformatting of comments. 90 | * Re-formatting the module exposings to match @docs tags in comments. 91 | 92 | ### Known deviations from `elm-format`: 93 | 94 | * Float literals not getting a .0 added at the end. 95 | * Type annotations on functions sometimes split onto next line without being 96 | broken themselves. 97 | 98 | ## Testing it. 99 | 100 | 1. Put some .elm files in `test/examples`. 101 | 2. Create directories `test/pre` and `test/post`. 102 | 3. In `test/` run `npm start`. 103 | 4. Diff `/pre` and `/post` to see how well it compares with elm-format. 104 | 105 | ## Some other thoughts. 106 | 107 | This could be used to create a command line tool `elm-pretty`. Needs to address the broken stuff first though. End-line comments is a deal breaker on `elm-syntax` too, so quite a bit of work to achieve this. 108 | 109 | Due to the above, this is aimed at use for code generation purposes for the present. 110 | 111 | Will always aim to be compatible with the latest `elm-format`, there is no point in introducing a new standard to compete with it. Any deviations from `elm-format` 112 | invariance please create issues or pull requests, which will be gratefully received. 113 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | ## Version 6.0.3 2 | 3 | Corrected printing \r and \u{hex code} strings and characters. 4 | 5 | ## Version 6.0.2 6 | 7 | Corrected the definition of the not equals binary operator (was typo "1/", corrected to "/="). 8 | 9 | ## Version 6.0.1 10 | 11 | Expressions now automatically add parenthesis where they are needed, so there 12 | is no longer any need to explicitly use `Elm.CodeGen.parens`. When adding an 13 | `Expression` into another `Expression`, the parenthesis will be added if the 14 | precedence rules of Elm require it. 15 | 16 | ## Version 6.0.0 17 | 18 | Upgraded to `the-sett/elm-pretty-printer` version 3.0.0. This tags parts 19 | of the pretty printer document to describe what kind of syntax it is; a 20 | keyword, operator, type and so on. The tags can then be used to apply 21 | fancy styling to the output; colour syntax highlighting being the main 22 | use case. 23 | 24 | ## Version 5.3.0 25 | 26 | Exposing `Elm.CodeGen.infixExpose`, so can expose custom operators from `elm/parser` and `elm/url`. 27 | 28 | Exposing `Elm.Pretty.prettyModule` for pretty printing module definitions on 29 | their own. 30 | 31 | ## Version 5.2.2 32 | 33 | Fixed printing of numbers in hex format - like '0x0F'. 34 | 35 | ## Version 5.2.1 36 | 37 | Fixed backslash escaping in `escapeChar` function. 38 | 39 | ## Version 5.2.0 40 | 41 | Added the `binOpChain` function, for chaining any binary operator. 42 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "the-sett/elm-syntax-dsl", 4 | "summary": "A DSL for creating Elm syntax trees and pretty printing Elm source code.", 5 | "license": "BSD-3-Clause", 6 | "version": "6.0.3", 7 | "exposed-modules": [ 8 | "Elm.CodeGen", 9 | "Elm.Pretty", 10 | "Elm.DSLParser" 11 | ], 12 | "elm-version": "0.19.0 <= v < 0.20.0", 13 | "dependencies": { 14 | "Chadtech/elm-bool-extra": "2.4.0 <= v < 3.0.0", 15 | "elm/core": "1.0.0 <= v < 2.0.0", 16 | "elm/parser": "1.1.0 <= v < 2.0.0", 17 | "elm-community/maybe-extra": "5.0.0 <= v < 6.0.0", 18 | "miniBill/elm-unicode": "1.1.1 <= v < 2.0.0", 19 | "rtfeldman/elm-hex": "1.0.0 <= v < 2.0.0", 20 | "stil4m/elm-syntax": "7.1.0 <= v < 8.0.0", 21 | "the-sett/elm-pretty-printer": "3.0.0 <= v < 4.0.0" 22 | }, 23 | "test-dependencies": { 24 | "elm-explorations/test": "2.2.0 <= v < 3.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Elm/CodeGen.elm: -------------------------------------------------------------------------------- 1 | module Elm.CodeGen exposing 2 | ( file 3 | , normalModule, portModule 4 | , exposeAll, exposeExplicit, infixExpose 5 | , closedTypeExpose, funExpose, openTypeExpose, typeOrAliasExpose 6 | , importStmt 7 | , Linkage, addExposing, addImport, combineLinkage, emptyLinkage 8 | , Comment, DocComment, FileComment, emptyDocComment, emptyFileComment, markdown, code, docTags, docTagsFromExposings 9 | , aliasDecl, customTypeDecl, funDecl, valDecl, portDecl 10 | , BinOp, composer, composel, power, mult, div, intDiv, modulo, remOp, plus 11 | , minus, append, cons, equals, notEqual, lt, gt, lte, gte, and, or, piper, pipel 12 | , binOp, applyBinOp, applyUnaryMinus 13 | , access, accessFun, apply, construct, caseExpr, char, float, fqConstruct, fqFun, fqVal, fun, glsl, hex 14 | , ifExpr, int, lambda, letExpr, list, negate, parens, record 15 | , string, tuple, unit, update, val 16 | , letFunction, letDestructuring, letVal 17 | , chain, pipe, binOpChain 18 | , allPattern, asPattern, charPattern, floatPattern, fqNamedPattern, hexPattern, intPattern 19 | , listPattern, namedPattern, parensPattern, recordPattern, stringPattern, tuplePattern, unConsPattern 20 | , unitPattern, varPattern 21 | , extRecordAnn, fqTyped, funAnn, recordAnn, tupleAnn, typeVar, typed, unitAnn 22 | , boolAnn, intAnn, floatAnn, stringAnn, charAnn 23 | , listAnn, setAnn, dictAnn, maybeAnn 24 | , signature 25 | , ModuleName, Module, File, Declaration(..), Import, TypeAnnotation 26 | , Exposing, TopLevelExpose, Expression, Pattern, LetDeclaration 27 | ) 28 | 29 | {-| Elm.CodeGen is a DSL designed to make it easier to write Elm code that generates Elm code. 30 | 31 | 32 | # Build an Elm source file. 33 | 34 | @docs file 35 | 36 | 37 | # Build a module declaration. 38 | 39 | `elm-syntax` also permits effects modules, but with kernel code restrictions you cannot use these so 40 | they have been ommitted from the DSL. 41 | 42 | @docs normalModule, portModule 43 | 44 | 45 | # Build an 'exposing' statement. 46 | 47 | @docs exposeAll, exposeExplicit, infixExpose 48 | @docs closedTypeExpose, funExpose, openTypeExpose, typeOrAliasExpose 49 | 50 | 51 | # Build an import statement. 52 | 53 | @docs importStmt 54 | 55 | 56 | # Incrementally build import and exposing statements. 57 | 58 | This is useful during code generation where the exact imports and exports are not known in advance 59 | but depend on what code is actually generated. Each section of code generation can declare the imports and 60 | exposings that it needs and they can be combined and de-duplicated to produce a final list. 61 | 62 | I have used the name `Linkage` to refer to the combination of a modules imports and exports. It describes 63 | how a module is linked to other modules. 64 | 65 | @docs Linkage, addExposing, addImport, combineLinkage, emptyLinkage 66 | 67 | 68 | # Build comments in a structured way. 69 | 70 | @docs Comment, DocComment, FileComment, emptyDocComment, emptyFileComment, markdown, code, docTags, docTagsFromExposings 71 | 72 | 73 | # Build top-level declarations. 74 | 75 | @docs aliasDecl, customTypeDecl, funDecl, valDecl, portDecl 76 | 77 | 78 | # Operators 79 | 80 | @docs BinOp, composer, composel, power, mult, div, intDiv, modulo, remOp, plus 81 | @docs minus, append, cons, equals, notEqual, lt, gt, lte, gte, and, or, piper, pipel 82 | @docs binOp, applyBinOp, applyUnaryMinus 83 | 84 | 85 | # Other Expressions. 86 | 87 | @docs access, accessFun, apply, construct, caseExpr, char, float, fqConstruct, fqFun, fqVal, fun, glsl, hex 88 | @docs ifExpr, int, lambda, letExpr, list, negate, parens, record 89 | @docs string, tuple, unit, update, val 90 | @docs letFunction, letDestructuring, letVal 91 | 92 | 93 | # Helper functions for common expression patterns. 94 | 95 | @docs chain, pipe, binOpChain 96 | 97 | 98 | # Build a pattern matching expression. 99 | 100 | @docs allPattern, asPattern, charPattern, floatPattern, fqNamedPattern, hexPattern, intPattern 101 | @docs listPattern, namedPattern, parensPattern, recordPattern, stringPattern, tuplePattern, unConsPattern 102 | @docs unitPattern, varPattern 103 | 104 | 105 | # Build a type annotation. 106 | 107 | @docs extRecordAnn, fqTyped, funAnn, recordAnn, tupleAnn, typeVar, typed, unitAnn 108 | @docs boolAnn, intAnn, floatAnn, stringAnn, charAnn 109 | @docs listAnn, setAnn, dictAnn, maybeAnn 110 | 111 | 112 | # Build a type signature. 113 | 114 | @docs signature 115 | 116 | 117 | # Types describing parts of the Elm AST. 118 | 119 | These types are all declared in `elm-syntax` but are re-exported here for convenience. 120 | 121 | @docs ModuleName, Module, File, Declaration, Import, TypeAnnotation 122 | @docs Exposing, TopLevelExpose, Expression, Pattern, LetDeclaration 123 | 124 | -} 125 | 126 | import Elm.Comments as Comments exposing (Comment, CommentPart(..), DocComment, FileComment) 127 | import Elm.Syntax.Declaration exposing (Declaration(..)) 128 | import Elm.Syntax.Exposing exposing (Exposing(..), TopLevelExpose(..)) 129 | import Elm.Syntax.Expression exposing (Case, CaseBlock, Expression(..), Function, FunctionImplementation, LetBlock, LetDeclaration(..), RecordSetter) 130 | import Elm.Syntax.File exposing (File) 131 | import Elm.Syntax.Import exposing (Import) 132 | import Elm.Syntax.Infix exposing (Infix, InfixDirection(..)) 133 | import Elm.Syntax.Module exposing (DefaultModuleData, EffectModuleData, Module(..)) 134 | import Elm.Syntax.ModuleName exposing (ModuleName) 135 | import Elm.Syntax.Node as Node exposing (Node) 136 | import Elm.Syntax.Pattern exposing (Pattern(..)) 137 | import Elm.Syntax.Range exposing (emptyRange) 138 | import Elm.Syntax.Signature exposing (Signature) 139 | import Elm.Syntax.Type exposing (Type, ValueConstructor) 140 | import Elm.Syntax.TypeAlias exposing (TypeAlias) 141 | import Elm.Syntax.TypeAnnotation exposing (RecordDefinition, RecordField, TypeAnnotation(..)) 142 | import Util exposing (nodify, nodifyAll, nodifyMaybe) 143 | 144 | 145 | 146 | --== Dynamic import and export lists. 147 | 148 | 149 | {-| Linkage describes the lists of imports and exposings of a module and allows 150 | these to be built up incrementally as code generation progresses. 151 | -} 152 | type alias Linkage = 153 | ( List Import, List TopLevelExpose ) 154 | 155 | 156 | {-| Simplifies the imports and exposings by removing any duplicates. 157 | -} 158 | combineLinkage : List Linkage -> Linkage 159 | combineLinkage importsAndExposings = 160 | let 161 | ( imports, exposings ) = 162 | List.unzip importsAndExposings 163 | in 164 | ( List.concat imports 165 | , List.concat exposings 166 | ) 167 | 168 | 169 | {-| Creates empty imports and exposings lists. 170 | -} 171 | emptyLinkage : Linkage 172 | emptyLinkage = 173 | ( [], [] ) 174 | 175 | 176 | {-| Adds an import to the list. 177 | -} 178 | addImport : Import -> Linkage -> Linkage 179 | addImport imp iande = 180 | Tuple.mapFirst ((::) imp) 181 | iande 182 | 183 | 184 | {-| Adds an exposing to the list. 185 | -} 186 | addExposing : TopLevelExpose -> Linkage -> Linkage 187 | addExposing tlExpose iande = 188 | Tuple.mapSecond ((::) tlExpose) 189 | iande 190 | 191 | 192 | 193 | --== Re-Export of Types 194 | 195 | 196 | {-| A module name can consist of mulitple Stirngs separated with '.'. 197 | -} 198 | type alias ModuleName = 199 | List String 200 | 201 | 202 | {-| The AST for an Elm module. 203 | -} 204 | type alias Module = 205 | Elm.Syntax.Module.Module 206 | 207 | 208 | {-| The AST for an Elm file. 209 | -} 210 | type alias File = 211 | { moduleDefinition : Node Module 212 | , imports : List (Node Import) 213 | , declarations : List Declaration 214 | , comments : Maybe (Comment FileComment) 215 | } 216 | 217 | 218 | {-| The AST for a top-level Elm declaration; a function, a value, a type or a 219 | type alias. 220 | -} 221 | type Declaration 222 | = DeclWithComment (Comment DocComment) (String -> Elm.Syntax.Declaration.Declaration) 223 | | DeclNoComment Elm.Syntax.Declaration.Declaration 224 | 225 | 226 | {-| The AST for an Elm import statement. 227 | -} 228 | type alias Import = 229 | Elm.Syntax.Import.Import 230 | 231 | 232 | {-| The AST for an Elm type annotation. 233 | -} 234 | type alias TypeAnnotation = 235 | Elm.Syntax.TypeAnnotation.TypeAnnotation 236 | 237 | 238 | {-| The AST for an Elm exposing statement. 239 | -} 240 | type alias Exposing = 241 | Elm.Syntax.Exposing.Exposing 242 | 243 | 244 | {-| The AST for a member of an Elm exposing statement. 245 | -} 246 | type alias TopLevelExpose = 247 | Elm.Syntax.Exposing.TopLevelExpose 248 | 249 | 250 | {-| The AST for an Elm expression. 251 | -} 252 | type alias Expression = 253 | Elm.Syntax.Expression.Expression 254 | 255 | 256 | {-| The AST for a de-structuring Elm pattern matching expression. 257 | -} 258 | type alias Pattern = 259 | Elm.Syntax.Pattern.Pattern 260 | 261 | 262 | {-| The AST for an Elm let declaration. 263 | -} 264 | type alias LetDeclaration = 265 | Elm.Syntax.Expression.LetDeclaration 266 | 267 | 268 | 269 | --== Comments 270 | 271 | 272 | {-| A structured representation of an Elm comment on a file or declaration. 273 | -} 274 | type alias Comment a = 275 | Comments.Comment a 276 | 277 | 278 | {-| A comment type for doc comments on top-level declarations. 279 | -} 280 | type alias DocComment = 281 | Comments.DocComment 282 | 283 | 284 | {-| A comment type for file comments at the head of a .elm file. 285 | -} 286 | type alias FileComment = 287 | Comments.FileComment 288 | 289 | 290 | {-| Creates an empty documenting comment that will go on a declaration. 291 | -} 292 | emptyDocComment : Comment DocComment 293 | emptyDocComment = 294 | Comments.emptyComment 295 | 296 | 297 | {-| Creates an empty comment that will go on a module file. 298 | -} 299 | emptyFileComment : Comment FileComment 300 | emptyFileComment = 301 | Comments.emptyComment 302 | 303 | 304 | {-| Adds some markdown to a comment. 305 | -} 306 | markdown : String -> Comment a -> Comment a 307 | markdown mdown comment = 308 | Markdown mdown 309 | |> Comments.addPart comment 310 | 311 | 312 | {-| Adds a code block to a comment. 313 | -} 314 | code : String -> Comment a -> Comment a 315 | code codeVal comment = 316 | Code codeVal 317 | |> Comments.addPart comment 318 | 319 | 320 | {-| Adds a set of doc tags to a comment. 321 | 322 | Doc tags will never be merged into a single line, but if they are too long to fit 323 | the page width, the pretty printer can break them into separate lines. 324 | 325 | -} 326 | docTags : List String -> Comment FileComment -> Comment FileComment 327 | docTags tags comment = 328 | DocTags tags 329 | |> Comments.addPart comment 330 | 331 | 332 | {-| Adds a set of doc tags taking from a description of what a module exposes, 333 | into a comment. 334 | 335 | Doc tags will never be merged into a single line, but if they are too long to fit 336 | the page width, the pretty printer can break them into separate lines. 337 | 338 | -} 339 | docTagsFromExposings : List TopLevelExpose -> Comment FileComment -> Comment FileComment 340 | docTagsFromExposings exposings comment = 341 | let 342 | asTag tlExpose = 343 | case tlExpose of 344 | InfixExpose name -> 345 | name 346 | 347 | FunctionExpose name -> 348 | name 349 | 350 | TypeOrAliasExpose name -> 351 | name 352 | 353 | TypeExpose exposedType -> 354 | exposedType.name 355 | in 356 | List.map asTag exposings 357 | |> DocTags 358 | |> Comments.addPart comment 359 | 360 | 361 | 362 | --== Elm.Syntax.Declaration 363 | 364 | 365 | {-| FunctionDeclaration Function 366 | -} 367 | funDecl : Maybe (Comment DocComment) -> Maybe TypeAnnotation -> String -> List Pattern -> Expression -> Declaration 368 | funDecl docs sig name args expr = 369 | case docs of 370 | Just docComment -> 371 | (\strDocs -> 372 | functionImplementation name args expr 373 | |> function (Just strDocs) (Maybe.map (signature name) sig) 374 | |> FunctionDeclaration 375 | ) 376 | |> DeclWithComment docComment 377 | 378 | Nothing -> 379 | functionImplementation name args expr 380 | |> function Nothing (Maybe.map (signature name) sig) 381 | |> FunctionDeclaration 382 | |> DeclNoComment 383 | 384 | 385 | {-| AliasDeclaration TypeAlias 386 | -} 387 | aliasDecl : Maybe (Comment DocComment) -> String -> List String -> TypeAnnotation -> Declaration 388 | aliasDecl docs name args annotation = 389 | case docs of 390 | Just docComment -> 391 | (\strDocs -> 392 | typeAlias (Just strDocs) name args annotation 393 | |> AliasDeclaration 394 | ) 395 | |> DeclWithComment docComment 396 | 397 | Nothing -> 398 | typeAlias Nothing name args annotation 399 | |> AliasDeclaration 400 | |> DeclNoComment 401 | 402 | 403 | {-| CustomTypeDeclaration Type 404 | -} 405 | customTypeDecl : Maybe (Comment DocComment) -> String -> List String -> List ( String, List TypeAnnotation ) -> Declaration 406 | customTypeDecl docs name args constructors = 407 | case docs of 408 | Just docComment -> 409 | (\strDocs -> 410 | customType (Just strDocs) name args constructors 411 | |> CustomTypeDeclaration 412 | ) 413 | |> DeclWithComment docComment 414 | 415 | Nothing -> 416 | customType Nothing name args constructors 417 | |> CustomTypeDeclaration 418 | |> DeclNoComment 419 | 420 | 421 | {-| PortDeclaration Signature 422 | -} 423 | portDecl : String -> TypeAnnotation -> Declaration 424 | portDecl name annotation = 425 | signature name annotation 426 | |> PortDeclaration 427 | |> DeclNoComment 428 | 429 | 430 | {-| A top-level value declaration or constant. 431 | -} 432 | valDecl : Maybe (Comment DocComment) -> Maybe TypeAnnotation -> String -> Expression -> Declaration 433 | valDecl docs sig name expr = 434 | case docs of 435 | Just docComment -> 436 | (\strDocs -> 437 | functionImplementation name [] expr 438 | |> function (Just strDocs) (Maybe.map (signature name) sig) 439 | |> FunctionDeclaration 440 | ) 441 | |> DeclWithComment docComment 442 | 443 | Nothing -> 444 | functionImplementation name [] expr 445 | |> function Nothing (Maybe.map (signature name) sig) 446 | |> FunctionDeclaration 447 | |> DeclNoComment 448 | 449 | 450 | 451 | --== Elm.Syntax.Exposing 452 | 453 | 454 | {-| All Range 455 | -} 456 | exposeAll : Exposing 457 | exposeAll = 458 | All emptyRange 459 | 460 | 461 | {-| Explicit (List (Node TopLevelExpose)) 462 | -} 463 | exposeExplicit : List TopLevelExpose -> Exposing 464 | exposeExplicit topLevelExposes = 465 | Explicit (nodifyAll topLevelExposes) 466 | 467 | 468 | {-| Custom operators were removed from Elm 0.19, but you can still import them 469 | from the kernel modules that do define them, such as elm/parser. 470 | -} 471 | infixExpose : String -> TopLevelExpose 472 | infixExpose sym = 473 | InfixExpose sym 474 | 475 | 476 | {-| FunctionExpose String 477 | -} 478 | funExpose : String -> TopLevelExpose 479 | funExpose fn = 480 | FunctionExpose fn 481 | 482 | 483 | {-| TypeOrAliasExpose String 484 | -} 485 | typeOrAliasExpose : String -> TopLevelExpose 486 | typeOrAliasExpose name = 487 | TypeOrAliasExpose name 488 | 489 | 490 | {-| TypeExpose ExposedType 491 | -} 492 | openTypeExpose : String -> TopLevelExpose 493 | openTypeExpose name = 494 | { name = name 495 | , open = Just emptyRange 496 | } 497 | |> TypeExpose 498 | 499 | 500 | {-| TypeExpose ExposedType 501 | -} 502 | closedTypeExpose : String -> TopLevelExpose 503 | closedTypeExpose name = 504 | { name = name 505 | , open = Nothing 506 | } 507 | |> TypeExpose 508 | 509 | 510 | 511 | --== Elm.Syntax.Expression 512 | 513 | 514 | {-| Joins multiple expressions together with the pipe operator `|>`. An 515 | expression `a` combined with a list of expressions `[b, c, d]` results in: 516 | 517 | a |> b |> c |> d 518 | 519 | -} 520 | pipe : Expression -> List Expression -> Expression 521 | pipe head expressions = 522 | case expressions of 523 | [] -> 524 | head 525 | 526 | [ expr ] -> 527 | applyBinOp head piper expr 528 | 529 | expr :: exprs -> 530 | applyBinOp head piper (pipe expr exprs) 531 | 532 | 533 | {-| Joins multiple expressions together with the function chain operator `>>`. An 534 | expression `a` combined with a list of expressions `[b, c, d]` results in: 535 | 536 | a >> b >> c >> d 537 | 538 | -} 539 | chain : Expression -> List Expression -> Expression 540 | chain head expressions = 541 | case expressions of 542 | [] -> 543 | head 544 | 545 | [ expr ] -> 546 | applyBinOp head composer expr 547 | 548 | expr :: exprs -> 549 | applyBinOp head composer (chain expr exprs) 550 | 551 | 552 | {-| Joins multiple expressions together with a binary operator. An 553 | expression `a`, and operator op, combined with a list of expressions `[b, c, d]` 554 | results in: 555 | 556 | a op b op c op d 557 | 558 | The expression is not bracketed so will parse as the operator associativity 559 | directs. 560 | 561 | -} 562 | binOpChain : Expression -> BinOp -> List Expression -> Expression 563 | binOpChain head binop expressions = 564 | case expressions of 565 | [] -> 566 | head 567 | 568 | [ expr ] -> 569 | applyBinOp head binop expr 570 | 571 | expr :: exprs -> 572 | applyBinOp head binop (binOpChain expr binop exprs) 573 | 574 | 575 | {-| UnitExpr 576 | -} 577 | unit : Expression 578 | unit = 579 | UnitExpr 580 | 581 | 582 | {-| Application (List (Node Expression)) 583 | -} 584 | apply : List Expression -> Expression 585 | apply exprs = 586 | Application (Util.nodifyAndParentifyAll 10 Left exprs) 587 | 588 | 589 | {-| Apply a named constructor to a list of arguments. 590 | -} 591 | construct : String -> List Expression -> Expression 592 | construct name args = 593 | apply (fun name :: args) 594 | 595 | 596 | {-| FunctionOrValue ModuleName String 597 | -} 598 | fqFun : ModuleName -> String -> Expression 599 | fqFun moduleName name = 600 | FunctionOrValue moduleName name 601 | 602 | 603 | {-| Apply a named constructor fully qualified with a module name, to a list of 604 | arguments. 605 | -} 606 | fqConstruct : ModuleName -> String -> List Expression -> Expression 607 | fqConstruct moduleName name args = 608 | apply (fqFun moduleName name :: args) 609 | 610 | 611 | {-| FunctionOrValue ModuleName String 612 | 613 | Note this is the same as `fqFun` 614 | 615 | -} 616 | fqVal : ModuleName -> String -> Expression 617 | fqVal moduleName name = 618 | FunctionOrValue moduleName name 619 | 620 | 621 | {-| Creates a FunctionOrValue with no qualifying module. 622 | -} 623 | fun : String -> Expression 624 | fun name = 625 | fqFun [] name 626 | 627 | 628 | {-| Creates a FunctionOrValue with no qualifying module. 629 | 630 | Note this is the same as `fun`. 631 | 632 | -} 633 | val : String -> Expression 634 | val name = 635 | fqVal [] name 636 | 637 | 638 | {-| IfBlock (Node Expression) (Node Expression) (Node Expression) 639 | -} 640 | ifExpr : Expression -> Expression -> Expression -> Expression 641 | ifExpr boolExpr trueExpr falseExpr = 642 | IfBlock (nodify boolExpr) (nodify trueExpr) (nodify falseExpr) 643 | 644 | 645 | {-| Integer Int 646 | -} 647 | int : Int -> Expression 648 | int intVal = 649 | Integer intVal 650 | 651 | 652 | {-| Hex Int 653 | -} 654 | hex : Int -> Expression 655 | hex hexVal = 656 | Hex hexVal 657 | 658 | 659 | {-| Floatable Float 660 | -} 661 | float : Float -> Expression 662 | float floatVal = 663 | Floatable floatVal 664 | 665 | 666 | {-| Negation (Node Expression) 667 | -} 668 | negate : Expression -> Expression 669 | negate expr = 670 | Negation (nodify expr) 671 | 672 | 673 | {-| Literal String 674 | -} 675 | string : String -> Expression 676 | string literal = 677 | Literal literal 678 | 679 | 680 | {-| CharLiteral Char 681 | -} 682 | char : Char -> Expression 683 | char charVal = 684 | CharLiteral charVal 685 | 686 | 687 | {-| TupledExpression (List (Node Expression)) 688 | -} 689 | tuple : List Expression -> Expression 690 | tuple exprs = 691 | TupledExpression (nodifyAll exprs) 692 | 693 | 694 | {-| ParenthesizedExpression (Node Expression) 695 | 696 | Note: elm-syntax-dsl should insert parentheses automatically in relevant places, however this can still be useful in corner cases. 697 | 698 | -} 699 | parens : Expression -> Expression 700 | parens expr = 701 | ParenthesizedExpression (nodify expr) 702 | 703 | 704 | {-| LetExpression LetBlock 705 | -} 706 | letExpr : List LetDeclaration -> Expression -> Expression 707 | letExpr declarations expr = 708 | letBlock declarations expr 709 | |> LetExpression 710 | 711 | 712 | {-| A function declared inside a let block. 713 | -} 714 | letFunction : String -> List Pattern -> Expression -> LetDeclaration 715 | letFunction name args expr = 716 | functionImplementation name args expr 717 | |> function Nothing Nothing 718 | |> LetFunction 719 | 720 | 721 | {-| A value declared inside a let block. 722 | -} 723 | letVal : String -> Expression -> LetDeclaration 724 | letVal valName expr = 725 | LetDestructuring (varPattern valName |> nodify) (nodify expr) 726 | 727 | 728 | {-| A pattern matching declared inside a let block. 729 | -} 730 | letDestructuring : Pattern -> Expression -> LetDeclaration 731 | letDestructuring pattern expr = 732 | LetDestructuring (nodify pattern) (nodify expr) 733 | 734 | 735 | {-| CaseExpression CaseBlock 736 | -} 737 | caseExpr : Expression -> List ( Pattern, Expression ) -> Expression 738 | caseExpr expr cases = 739 | List.map (\( pat, body ) -> case_ pat body) cases 740 | |> caseBlock expr 741 | |> CaseExpression 742 | 743 | 744 | {-| LambdaExpression Lambda 745 | -} 746 | lambda : List Pattern -> Expression -> Expression 747 | lambda args expr = 748 | { args = nodifyAll args, expression = nodify expr } 749 | |> LambdaExpression 750 | 751 | 752 | {-| RecordExpr (List (Node RecordSetter)) 753 | -} 754 | record : List ( String, Expression ) -> Expression 755 | record setters = 756 | List.map (\( fieldName, expr ) -> recordSetter fieldName expr) setters 757 | |> nodifyAll 758 | |> RecordExpr 759 | 760 | 761 | {-| ListExpr (List (Node Expression)) 762 | -} 763 | list : List Expression -> Expression 764 | list exprs = 765 | ListExpr (nodifyAll exprs) 766 | 767 | 768 | {-| RecordAccess (Node Expression) (Node String) 769 | -} 770 | access : Expression -> String -> Expression 771 | access expr selector = 772 | RecordAccess (nodify (Util.parentify 10 Left expr)) (nodify selector) 773 | 774 | 775 | {-| RecordAccessFunction String 776 | -} 777 | accessFun : String -> Expression 778 | accessFun selector = 779 | RecordAccessFunction selector 780 | 781 | 782 | {-| RecordUpdateExpression (Node String) (List (Node RecordSetter)) 783 | -} 784 | update : String -> List ( String, Expression ) -> Expression 785 | update varName setters = 786 | List.map (\( fieldName, expr ) -> recordSetter fieldName expr) setters 787 | |> nodifyAll 788 | |> RecordUpdateExpression (nodify varName) 789 | 790 | 791 | {-| GLSLExpression String 792 | -} 793 | glsl : String -> Expression 794 | glsl expr = 795 | GLSLExpression expr 796 | 797 | 798 | letBlock : List LetDeclaration -> Expression -> LetBlock 799 | letBlock decls expr = 800 | { declarations = nodifyAll decls 801 | , expression = nodify expr 802 | } 803 | 804 | 805 | recordSetter : String -> Expression -> RecordSetter 806 | recordSetter field expr = 807 | ( nodify field, nodify expr ) 808 | 809 | 810 | caseBlock : Expression -> List Case -> CaseBlock 811 | caseBlock expr cases = 812 | { expression = nodify expr 813 | , cases = cases 814 | } 815 | 816 | 817 | case_ : Pattern -> Expression -> Case 818 | case_ pattern expr = 819 | ( nodify pattern, nodify expr ) 820 | 821 | 822 | function : Maybe String -> Maybe Signature -> FunctionImplementation -> Function 823 | function docs sig decl = 824 | { documentation = nodifyMaybe docs 825 | , signature = nodifyMaybe sig 826 | , declaration = nodify decl 827 | } 828 | 829 | 830 | functionImplementation : String -> List Pattern -> Expression -> FunctionImplementation 831 | functionImplementation name args expr = 832 | { name = nodify name 833 | , arguments = nodifyAll args 834 | , expression = nodify expr 835 | } 836 | 837 | 838 | 839 | --== Operators 840 | 841 | 842 | {-| Represents all of the binary operators allowed in Elm. 843 | -} 844 | type BinOp 845 | = BinOp String InfixDirection Int 846 | 847 | 848 | {-| The compose right operator `>>`. 849 | -} 850 | composer : BinOp 851 | composer = 852 | BinOp ">>" infixLeft 9 853 | 854 | 855 | {-| The compose left operator `<<`. 856 | -} 857 | composel : BinOp 858 | composel = 859 | BinOp "<<" infixRight 9 860 | 861 | 862 | {-| The to-the-power-of operator `^` 863 | -} 864 | power : BinOp 865 | power = 866 | BinOp "^" infixRight 8 867 | 868 | 869 | {-| The multiplication operator `*`. 870 | -} 871 | mult : BinOp 872 | mult = 873 | BinOp "*" infixLeft 7 874 | 875 | 876 | {-| The division operator `/`. 877 | -} 878 | div : BinOp 879 | div = 880 | BinOp "/" infixLeft 7 881 | 882 | 883 | {-| The integer division operator `//`. 884 | -} 885 | intDiv : BinOp 886 | intDiv = 887 | BinOp "//" infixLeft 7 888 | 889 | 890 | {-| The modulo operator `%`. 891 | -} 892 | modulo : BinOp 893 | modulo = 894 | BinOp "%" infixLeft 7 895 | 896 | 897 | {-| The remainder operator `rem`. 898 | -} 899 | remOp : BinOp 900 | remOp = 901 | BinOp "rem" infixLeft 7 902 | 903 | 904 | {-| The addition operator `+`. 905 | -} 906 | plus : BinOp 907 | plus = 908 | BinOp "+" infixLeft 6 909 | 910 | 911 | {-| The subtraction operator `-`. 912 | -} 913 | minus : BinOp 914 | minus = 915 | BinOp "-" infixLeft 6 916 | 917 | 918 | {-| The append operator `++`. 919 | -} 920 | append : BinOp 921 | append = 922 | BinOp "++" infixRight 5 923 | 924 | 925 | {-| The cons operator `::`. 926 | -} 927 | cons : BinOp 928 | cons = 929 | BinOp "::" infixRight 5 930 | 931 | 932 | {-| The equality operator `==`. 933 | -} 934 | equals : BinOp 935 | equals = 936 | BinOp "==" infixLeft 4 937 | 938 | 939 | {-| The inequality operator `/=`. 940 | -} 941 | notEqual : BinOp 942 | notEqual = 943 | BinOp "/=" infixLeft 4 944 | 945 | 946 | {-| The less-than operator `<`. 947 | -} 948 | lt : BinOp 949 | lt = 950 | BinOp "<" infixNon 4 951 | 952 | 953 | {-| The greater-than operator `>`. 954 | -} 955 | gt : BinOp 956 | gt = 957 | BinOp ">" infixNon 4 958 | 959 | 960 | {-| The less-than-or-equal operator `<=`. 961 | -} 962 | lte : BinOp 963 | lte = 964 | BinOp "<=" infixNon 4 965 | 966 | 967 | {-| The greater-than-or-equal operator `>=`. 968 | -} 969 | gte : BinOp 970 | gte = 971 | BinOp ">=" infixNon 4 972 | 973 | 974 | {-| The logical and operator `&&`. 975 | -} 976 | and : BinOp 977 | and = 978 | BinOp "&&" infixRight 3 979 | 980 | 981 | {-| The logical or operator `||`. 982 | -} 983 | or : BinOp 984 | or = 985 | BinOp "||" infixRight 2 986 | 987 | 988 | {-| The pipe right operator `|>`. 989 | -} 990 | piper : BinOp 991 | piper = 992 | BinOp "|>" infixLeft 0 993 | 994 | 995 | {-| The pipe left operator `<|`. 996 | -} 997 | pipel : BinOp 998 | pipel = 999 | BinOp "<|" infixRight 0 1000 | 1001 | 1002 | {-| Creates a binary operator in its prefix form, as a bracketed expression. 1003 | 1004 | binOp equals 1005 | 1006 | Yields: 1007 | 1008 | (=) 1009 | 1010 | -} 1011 | binOp : BinOp -> Expression 1012 | binOp (BinOp symbol _ _) = 1013 | Operator symbol 1014 | 1015 | 1016 | {-| Applies a binary operator to left and right expressions. This takes the 1017 | expression in the order that is usual; that being `expr op expr`. 1018 | 1019 | applyBinOp (int 2) plus (int 3) 1020 | 1021 | Yields: 1022 | 1023 | 2 + 3 1024 | 1025 | -} 1026 | applyBinOp : Expression -> BinOp -> Expression -> Expression 1027 | applyBinOp exprl (BinOp symbol dir precedence) exprr = 1028 | OperatorApplication symbol dir (nodify (Util.parentify precedence dir exprl)) (nodify (Util.parentify precedence dir exprr)) 1029 | 1030 | 1031 | {-| There is only one unary operator in Elm, and that is the minus sign prefixed 1032 | onto some numeric expression. 1033 | 1034 | applyUnaryMinus (int 5) 1035 | 1036 | Yields: 1037 | 1038 | -5 1039 | 1040 | -} 1041 | applyUnaryMinus : Expression -> Expression 1042 | applyUnaryMinus expr = 1043 | apply [ PrefixOperator "-", expr ] 1044 | 1045 | 1046 | infix_ : InfixDirection -> Int -> String -> String -> Infix 1047 | infix_ direction precedence symbol fn = 1048 | { direction = nodify direction 1049 | , precedence = nodify precedence 1050 | , operator = nodify symbol 1051 | , function = nodify fn 1052 | } 1053 | 1054 | 1055 | {-| Denotes an infix operator that is left associative. 1056 | -} 1057 | infixLeft : InfixDirection 1058 | infixLeft = 1059 | Left 1060 | 1061 | 1062 | {-| Denotes an infix operator that is right associative. 1063 | -} 1064 | infixRight : InfixDirection 1065 | infixRight = 1066 | Right 1067 | 1068 | 1069 | {-| Denotes an infix operator that is non-associative. 1070 | -} 1071 | infixNon : InfixDirection 1072 | infixNon = 1073 | Non 1074 | 1075 | 1076 | 1077 | --== Elm.Syntax.File 1078 | 1079 | 1080 | {-| Assembles all the components of an Elm file; the module declaration, the 1081 | comments, the imports and the top-level declarations. 1082 | -} 1083 | file : Module -> List Import -> List Declaration -> Maybe (Comment FileComment) -> File 1084 | file mod imports declarations docs = 1085 | { moduleDefinition = nodify mod 1086 | , imports = nodifyAll imports 1087 | , declarations = declarations 1088 | , comments = docs 1089 | } 1090 | 1091 | 1092 | 1093 | --== Elm.Syntax.Import 1094 | 1095 | 1096 | {-| Creates an Elm import statement; the name of the module, an optional alias 1097 | name for the module, and an optional list of exposings from the module. 1098 | -} 1099 | importStmt : ModuleName -> Maybe ModuleName -> Maybe Exposing -> Import 1100 | importStmt modName aliasName exposes = 1101 | { moduleName = nodify modName 1102 | , moduleAlias = nodifyMaybe aliasName 1103 | , exposingList = nodifyMaybe exposes 1104 | } 1105 | 1106 | 1107 | 1108 | --== Elm.Syntax.Module 1109 | 1110 | 1111 | {-| NormalModule DefaultModuleData 1112 | -} 1113 | normalModule : ModuleName -> List TopLevelExpose -> Module 1114 | normalModule name exposes = 1115 | NormalModule <| defaultModuleData name (exposing_ exposes) 1116 | 1117 | 1118 | {-| PortModule DefaultModuleData 1119 | -} 1120 | portModule : ModuleName -> List TopLevelExpose -> Module 1121 | portModule name exposes = 1122 | PortModule <| defaultModuleData name (exposing_ exposes) 1123 | 1124 | 1125 | effectModule : ModuleName -> List TopLevelExpose -> Maybe String -> Maybe String -> Module 1126 | effectModule name exposes cmd sub = 1127 | EffectModule <| effectModuleData name (exposing_ exposes) cmd sub 1128 | 1129 | 1130 | defaultModuleData : ModuleName -> Exposing -> DefaultModuleData 1131 | defaultModuleData name exposes = 1132 | { moduleName = nodify name 1133 | , exposingList = nodify exposes 1134 | } 1135 | 1136 | 1137 | effectModuleData : ModuleName -> Exposing -> Maybe String -> Maybe String -> EffectModuleData 1138 | effectModuleData name exposes cmd sub = 1139 | { moduleName = nodify name 1140 | , exposingList = nodify exposes 1141 | , command = nodifyMaybe cmd 1142 | , subscription = nodifyMaybe sub 1143 | } 1144 | 1145 | 1146 | 1147 | --== Elm.Syntax.Pattern 1148 | 1149 | 1150 | {-| AllPattern 1151 | -} 1152 | allPattern : Pattern 1153 | allPattern = 1154 | AllPattern 1155 | 1156 | 1157 | {-| UnitPattern 1158 | -} 1159 | unitPattern : Pattern 1160 | unitPattern = 1161 | UnitPattern 1162 | 1163 | 1164 | {-| CharPattern Char 1165 | -} 1166 | charPattern : Char -> Pattern 1167 | charPattern charVal = 1168 | CharPattern charVal 1169 | 1170 | 1171 | {-| StringPattern String 1172 | -} 1173 | stringPattern : String -> Pattern 1174 | stringPattern literal = 1175 | StringPattern literal 1176 | 1177 | 1178 | {-| IntPattern Int 1179 | -} 1180 | intPattern : Int -> Pattern 1181 | intPattern intVal = 1182 | IntPattern intVal 1183 | 1184 | 1185 | {-| HexPattern Int 1186 | -} 1187 | hexPattern : Int -> Pattern 1188 | hexPattern hexVal = 1189 | HexPattern hexVal 1190 | 1191 | 1192 | {-| FloatPattern Float 1193 | -} 1194 | floatPattern : Float -> Pattern 1195 | floatPattern floatVal = 1196 | FloatPattern floatVal 1197 | 1198 | 1199 | {-| TuplePattern (List (Node Pattern)) 1200 | -} 1201 | tuplePattern : List Pattern -> Pattern 1202 | tuplePattern patterns = 1203 | TuplePattern (nodifyAll patterns) 1204 | 1205 | 1206 | {-| RecordPattern (List (Node String)) 1207 | -} 1208 | recordPattern : List String -> Pattern 1209 | recordPattern fields = 1210 | RecordPattern (nodifyAll fields) 1211 | 1212 | 1213 | {-| UnConsPattern (Node Pattern) (Node Pattern) 1214 | -} 1215 | unConsPattern : Pattern -> Pattern -> Pattern 1216 | unConsPattern hd tl = 1217 | UnConsPattern (nodify hd) (nodify tl) 1218 | 1219 | 1220 | {-| ListPattern (List (Node Pattern)) 1221 | -} 1222 | listPattern : List Pattern -> Pattern 1223 | listPattern seq = 1224 | ListPattern (nodifyAll seq) 1225 | 1226 | 1227 | {-| VarPattern String 1228 | -} 1229 | varPattern : String -> Pattern 1230 | varPattern name = 1231 | VarPattern name 1232 | 1233 | 1234 | {-| NamedPattern QualifiedNameRef (List (Node Pattern)) 1235 | -} 1236 | namedPattern : String -> List Pattern -> Pattern 1237 | namedPattern name patterns = 1238 | NamedPattern { moduleName = [], name = name } (nodifyAll patterns) 1239 | 1240 | 1241 | {-| NamedPattern QualifiedNameRef (List (Node Pattern)) 1242 | -} 1243 | fqNamedPattern : ModuleName -> String -> List Pattern -> Pattern 1244 | fqNamedPattern moduleName name patterns = 1245 | NamedPattern { moduleName = moduleName, name = name } (nodifyAll patterns) 1246 | 1247 | 1248 | {-| AsPattern (Node Pattern) (Node String) 1249 | -} 1250 | asPattern : Pattern -> String -> Pattern 1251 | asPattern pattern name = 1252 | AsPattern (nodify pattern) (nodify name) 1253 | 1254 | 1255 | {-| ParenthesizedPattern (Node Pattern) 1256 | -} 1257 | parensPattern : Pattern -> Pattern 1258 | parensPattern pattern = 1259 | ParenthesizedPattern (nodify pattern) 1260 | 1261 | 1262 | 1263 | --== Elm.Syntax.Signature 1264 | 1265 | 1266 | {-| Creates a type signature. 1267 | -} 1268 | signature : String -> TypeAnnotation -> Signature 1269 | signature name annotation = 1270 | { name = nodify name 1271 | , typeAnnotation = nodify annotation 1272 | } 1273 | 1274 | 1275 | 1276 | --== Elm.Syntax.Type 1277 | 1278 | 1279 | customType : Maybe String -> String -> List String -> List ( String, List TypeAnnotation ) -> Type 1280 | customType docs name args constructors = 1281 | let 1282 | vcons = 1283 | List.map (\( consName, annotation ) -> valueConstructor consName annotation) constructors 1284 | in 1285 | { documentation = nodifyMaybe docs 1286 | , name = nodify name 1287 | , generics = nodifyAll args 1288 | , constructors = nodifyAll vcons 1289 | } 1290 | 1291 | 1292 | valueConstructor : String -> List TypeAnnotation -> ValueConstructor 1293 | valueConstructor name annotations = 1294 | { name = nodify name 1295 | , arguments = nodifyAll annotations 1296 | } 1297 | 1298 | 1299 | 1300 | --== Elm.Syntax.TypeAlias 1301 | 1302 | 1303 | typeAlias : Maybe String -> String -> List String -> TypeAnnotation -> TypeAlias 1304 | typeAlias docs name args annotation = 1305 | { documentation = nodifyMaybe docs 1306 | , name = nodify name 1307 | , generics = nodifyAll args 1308 | , typeAnnotation = nodify annotation 1309 | } 1310 | 1311 | 1312 | 1313 | --== Elm.Syntax.TypeAnnotation 1314 | 1315 | 1316 | {-| GenericType String 1317 | -} 1318 | typeVar : String -> TypeAnnotation 1319 | typeVar name = 1320 | GenericType name 1321 | 1322 | 1323 | {-| Typed (Node ( ModuleName, String )) (List (Node TypeAnnotation)) 1324 | -} 1325 | fqTyped : ModuleName -> String -> List TypeAnnotation -> TypeAnnotation 1326 | fqTyped moduleName name args = 1327 | Typed (nodify ( moduleName, name )) (nodifyAll args) 1328 | 1329 | 1330 | {-| Typed (Node ( ModuleName, String )) (List (Node TypeAnnotation)) 1331 | -} 1332 | typed : String -> List TypeAnnotation -> TypeAnnotation 1333 | typed name args = 1334 | Typed (nodify ( [], name )) (nodifyAll args) 1335 | 1336 | 1337 | {-| Unit 1338 | -} 1339 | unitAnn : TypeAnnotation 1340 | unitAnn = 1341 | Unit 1342 | 1343 | 1344 | {-| Tupled (List (Node TypeAnnotation)) 1345 | -} 1346 | tupleAnn : List TypeAnnotation -> TypeAnnotation 1347 | tupleAnn args = 1348 | Tupled (nodifyAll args) 1349 | 1350 | 1351 | {-| Record RecordDefinition 1352 | -} 1353 | recordAnn : List ( String, TypeAnnotation ) -> TypeAnnotation 1354 | recordAnn fields = 1355 | List.map (uncurry recordField) fields 1356 | |> recordDefinition 1357 | |> Record 1358 | 1359 | 1360 | {-| GenericRecord (Node String) (Node RecordDefinition) 1361 | -} 1362 | extRecordAnn : String -> List ( String, TypeAnnotation ) -> TypeAnnotation 1363 | extRecordAnn argName fields = 1364 | List.map (uncurry recordField) fields 1365 | |> recordDefinition 1366 | |> nodify 1367 | |> GenericRecord (nodify argName) 1368 | 1369 | 1370 | {-| FunctionTypeAnnotation (Node TypeAnnotation) (Node TypeAnnotation) 1371 | -} 1372 | funAnn : TypeAnnotation -> TypeAnnotation -> TypeAnnotation 1373 | funAnn arg result = 1374 | FunctionTypeAnnotation (nodify arg) (nodify result) 1375 | 1376 | 1377 | {-| A `Bool` type annotation. 1378 | -} 1379 | boolAnn : TypeAnnotation 1380 | boolAnn = 1381 | typed "Bool" [] 1382 | 1383 | 1384 | {-| An `Int` type annotation. 1385 | -} 1386 | intAnn : TypeAnnotation 1387 | intAnn = 1388 | typed "Int" [] 1389 | 1390 | 1391 | {-| A `Float` type annotation. 1392 | -} 1393 | floatAnn : TypeAnnotation 1394 | floatAnn = 1395 | typed "Float" [] 1396 | 1397 | 1398 | {-| A `String` type annotation. 1399 | -} 1400 | stringAnn : TypeAnnotation 1401 | stringAnn = 1402 | typed "String" [] 1403 | 1404 | 1405 | {-| A `Char` type annotation. 1406 | -} 1407 | charAnn : TypeAnnotation 1408 | charAnn = 1409 | typed "Char" [] 1410 | 1411 | 1412 | {-| Creates a `List` type annotation. 1413 | -} 1414 | listAnn : TypeAnnotation -> TypeAnnotation 1415 | listAnn listArg = 1416 | typed "List" [ listArg ] 1417 | 1418 | 1419 | {-| Creates a `Set` type annotation. 1420 | -} 1421 | setAnn : TypeAnnotation -> TypeAnnotation 1422 | setAnn setArg = 1423 | typed "Set" [ setArg ] 1424 | 1425 | 1426 | {-| Creates a `Dict` type annotation. 1427 | -} 1428 | dictAnn : TypeAnnotation -> TypeAnnotation -> TypeAnnotation 1429 | dictAnn keyArg valArg = 1430 | typed "Dict" [ keyArg, valArg ] 1431 | 1432 | 1433 | {-| Creates a `Maybe` type annotation. 1434 | -} 1435 | maybeAnn : TypeAnnotation -> TypeAnnotation 1436 | maybeAnn maybeArg = 1437 | typed "Maybe" [ maybeArg ] 1438 | 1439 | 1440 | recordDefinition : List RecordField -> RecordDefinition 1441 | recordDefinition fields = 1442 | nodifyAll fields 1443 | 1444 | 1445 | recordField : String -> TypeAnnotation -> RecordField 1446 | recordField field typeAnnotation = 1447 | ( nodify field, nodify typeAnnotation ) 1448 | 1449 | 1450 | 1451 | --== Helpers 1452 | 1453 | 1454 | uncurry : (a -> b -> c) -> ( a, b ) -> c 1455 | uncurry fn ( a, b ) = 1456 | fn a b 1457 | 1458 | 1459 | exposing_ : List TopLevelExpose -> Exposing 1460 | exposing_ exposes = 1461 | case exposes of 1462 | [] -> 1463 | All emptyRange 1464 | 1465 | es -> 1466 | Explicit <| nodifyAll exposes 1467 | -------------------------------------------------------------------------------- /src/Elm/Comments.elm: -------------------------------------------------------------------------------- 1 | module Elm.Comments exposing 2 | ( Comment(..), CommentPart(..), DocComment, FileComment 3 | , emptyComment, addPart 4 | ) 5 | 6 | {-| A component DSL that helps with building comments. 7 | 8 | It is useful to have this in a structured way, so that it can be re-flowed by 9 | the pretty printer, and so that an understanding of the layout of the doc tags 10 | can be extracted to order the exposing clause by. 11 | 12 | 13 | # Structured comments 14 | 15 | @docs Comment, CommentPart, DocComment, FileComment 16 | 17 | 18 | # Building comments 19 | 20 | @docs emptyComment, addPart 21 | 22 | 23 | # Pretty printing of comments 24 | 25 | @docs prettyDocComment, prettyFileComment 26 | 27 | 28 | # Parsing of comments into structured comments 29 | 30 | @docs docCommentParser, fileCommentParser 31 | 32 | -} 33 | 34 | import Parser exposing (Parser) 35 | import Pretty exposing (Doc) 36 | 37 | 38 | 39 | --import Elm.Token exposing (Token) 40 | 41 | 42 | type DocComment 43 | = DocComment 44 | 45 | 46 | type FileComment 47 | = FileComment 48 | 49 | 50 | type Comment a 51 | = Comment (List CommentPart) 52 | 53 | 54 | type CommentPart 55 | = Markdown String 56 | | Code String 57 | | DocTags (List String) 58 | 59 | 60 | {-| Creates an empty comment of any type. 61 | -} 62 | emptyComment : Comment a 63 | emptyComment = 64 | Comment [] 65 | 66 | 67 | {-| Adds a part to a comment. 68 | -} 69 | addPart : Comment a -> CommentPart -> Comment a 70 | addPart (Comment parts) part = 71 | Comment (part :: parts) 72 | -------------------------------------------------------------------------------- /src/Elm/DSLParser.elm: -------------------------------------------------------------------------------- 1 | module Elm.DSLParser exposing (parse) 2 | 3 | {-| An Elm source code parser. 4 | 5 | This differs from the parser in Elm.Parser which is part of `stil4m/elm-syntax`, 6 | in that it does not parse comments just as strings, but into a structured format. 7 | 8 | The structured format know the difference between different parts of the comment 9 | markdown including descriptive text, code examples and docs tags. The structured 10 | format is used to re-flow comments to fit a page width, and to use doc tags to 11 | determine how to lay out exposing lists to match. 12 | 13 | @docs parse 14 | 15 | -} 16 | 17 | --exposing ((|.), (|=)) 18 | 19 | import Elm.CodeGen as CG exposing (Declaration, File) 20 | import Elm.Comments as Comments exposing (Comment, CommentPart(..)) 21 | import Elm.Parser 22 | import Elm.Processing 23 | import Elm.Syntax.Declaration as ESD 24 | import Elm.Syntax.File 25 | import Parser exposing ((|.), (|=), DeadEnd, Parser) 26 | import Util exposing (denode, denodeAll, denodeMaybe, nodify) 27 | 28 | 29 | {-| Parses a string into a file of Elm code. 30 | -} 31 | parse : String -> Result (List DeadEnd) File 32 | parse val = 33 | Elm.Parser.parse val 34 | |> Result.map (Elm.Processing.process Elm.Processing.init) 35 | |> Result.map parseFileComments 36 | 37 | 38 | parseFileComments : Elm.Syntax.File.File -> File 39 | parseFileComments file = 40 | let 41 | fileComments = 42 | List.reverse file.comments 43 | |> denodeAll 44 | |> findFirstComment 45 | in 46 | CG.file (denode file.moduleDefinition) 47 | (denodeAll file.imports) 48 | (List.map parseDeclComments (denodeAll file.declarations)) 49 | fileComments 50 | 51 | 52 | parseDeclComments : ESD.Declaration -> Declaration 53 | parseDeclComments decl = 54 | let 55 | docComments = 56 | docsFromDecl decl 57 | |> Maybe.andThen tryParseComment 58 | in 59 | case docComments of 60 | Nothing -> 61 | CG.DeclNoComment (declNoDocs decl) 62 | 63 | Just val -> 64 | CG.DeclWithComment val (declWithDocs decl) 65 | 66 | 67 | findFirstComment : List String -> Maybe (Comment a) 68 | findFirstComment comments = 69 | case comments of 70 | [] -> 71 | Nothing 72 | 73 | c :: cs -> 74 | case Parser.run commentParser c of 75 | Err _ -> 76 | findFirstComment cs 77 | 78 | Ok val -> 79 | Just val 80 | 81 | 82 | tryParseComment : String -> Maybe (Comment a) 83 | tryParseComment comment = 84 | Parser.run commentParser comment |> Result.toMaybe 85 | 86 | 87 | commentParser : Parser (Comment a) 88 | commentParser = 89 | Parser.succeed 90 | (\val -> Comments.addPart Comments.emptyComment (removeDelims val |> Markdown)) 91 | |= (Parser.multiComment "{-|" "-}" Parser.Nestable |> Parser.getChompedString) 92 | 93 | 94 | removeDelims : String -> String 95 | removeDelims val = 96 | String.slice 3 -2 val 97 | |> String.trim 98 | 99 | 100 | {-| Replaces the documentation on a declaration with the specified string. 101 | -} 102 | declWithDocs : ESD.Declaration -> String -> ESD.Declaration 103 | declWithDocs decl docs = 104 | case decl of 105 | ESD.FunctionDeclaration funDecl -> 106 | ESD.FunctionDeclaration 107 | { funDecl | documentation = nodify docs |> Just } 108 | 109 | ESD.AliasDeclaration aliasDecl -> 110 | ESD.AliasDeclaration { aliasDecl | documentation = nodify docs |> Just } 111 | 112 | ESD.CustomTypeDeclaration customTypeDecl -> 113 | ESD.CustomTypeDeclaration { customTypeDecl | documentation = nodify docs |> Just } 114 | 115 | _ -> 116 | decl 117 | 118 | 119 | {-| Strips the docs from a declaration. 120 | -} 121 | declNoDocs : ESD.Declaration -> ESD.Declaration 122 | declNoDocs decl = 123 | case decl of 124 | ESD.FunctionDeclaration funDecl -> 125 | ESD.FunctionDeclaration 126 | { funDecl | documentation = Nothing } 127 | 128 | ESD.AliasDeclaration aliasDecl -> 129 | ESD.AliasDeclaration { aliasDecl | documentation = Nothing } 130 | 131 | ESD.CustomTypeDeclaration customTypeDecl -> 132 | ESD.CustomTypeDeclaration { customTypeDecl | documentation = Nothing } 133 | 134 | _ -> 135 | decl 136 | 137 | 138 | {-| Extracts the documentation from a declaration as a string. 139 | -} 140 | docsFromDecl : ESD.Declaration -> Maybe String 141 | docsFromDecl decl = 142 | case decl of 143 | ESD.FunctionDeclaration funDecl -> 144 | denodeMaybe funDecl.documentation 145 | 146 | ESD.AliasDeclaration aliasDecl -> 147 | denodeMaybe aliasDecl.documentation 148 | 149 | ESD.CustomTypeDeclaration customTypeDecl -> 150 | denodeMaybe customTypeDecl.documentation 151 | 152 | _ -> 153 | Nothing 154 | -------------------------------------------------------------------------------- /src/Elm/Pretty.elm: -------------------------------------------------------------------------------- 1 | module Elm.Pretty exposing 2 | ( Tag(..) 3 | , prepareLayout, pretty 4 | , prettyModule 5 | , prettyImports, prettyExposing 6 | , prettyDeclaration, prettyFun, prettyTypeAlias, prettyCustomType, prettyPortDeclaration, prettyDestructuring 7 | , prettySignature, prettyPattern, prettyExpression, prettyTypeAnnotation 8 | ) 9 | 10 | {-| Elm.Pretty is a pretty printer for Elm syntax trees. It makes use of 11 | `the-sett/elm-pretty-printer` to best fit the code to a given page width in 12 | characters. 13 | 14 | It aims to output code that is fully stable with respect to `elm-format` in the 15 | sense that running `elm-format` on the output should have no effect at all. The 16 | advantage of this is that if generated code moves to being edited by hand, there 17 | will not be a large white-space only diff created when `elm-format` is applied. 18 | 19 | To print the `Doc` created by the `pretty` functions, `the-sett/elm-pretty-printer` 20 | is used: 21 | 22 | import Elm.Pretty 23 | import Pretty 24 | 25 | 26 | -- Fit to a page width of 120 characters 27 | elmAsString = 28 | Elm.Pretty.prepareLayout 120 someFile 29 | |> Pretty.pretty 120 30 | 31 | Use the `Pretty.Renderer` module to consume the `Tag`s when printing to create 32 | fancy outputs such as HTML or console colors for syntax highlighting: 33 | 34 | -- Fit to a column width of 120 characters 35 | elmAsHtmlWithHighlighting = 36 | Elm.Pretty.prepareLayout 120 someFile 37 | |> Pretty.Renderer.pretty htmlRenderer 120 38 | 39 | There is also a helper `pretty` function in this module that can go straight to 40 | a `String`, for convenience: 41 | 42 | -- Fit to a page width of 120 characters 43 | elmAsString = 44 | Elm.Pretty.pretty 120 someFile 45 | 46 | 47 | # Syntax highlighting tags. 48 | 49 | @docs Tag 50 | 51 | 52 | # Pretty prints an entire Elm file. 53 | 54 | @docs prepareLayout, pretty 55 | 56 | 57 | # Pretty printing snippets of Elm. 58 | 59 | @docs prettyModule 60 | @docs prettyImports, prettyExposing 61 | @docs prettyDeclaration, prettyFun, prettyTypeAlias, prettyCustomType, prettyPortDeclaration, prettyDestructuring 62 | @docs prettySignature, prettyPattern, prettyExpression, prettyTypeAnnotation 63 | 64 | -} 65 | 66 | import Bool.Extra 67 | import Elm.CodeGen exposing (Declaration(..), File) 68 | import Elm.Comments as Comments 69 | import Elm.Syntax.Declaration 70 | import Elm.Syntax.Documentation exposing (Documentation) 71 | import Elm.Syntax.Exposing exposing (Exposing(..), TopLevelExpose(..)) 72 | import Elm.Syntax.Expression exposing (CaseBlock, Expression(..), Function, FunctionImplementation, Lambda, LetBlock, LetDeclaration(..), RecordSetter) 73 | import Elm.Syntax.File 74 | import Elm.Syntax.Import exposing (Import) 75 | import Elm.Syntax.Infix exposing (Infix, InfixDirection(..)) 76 | import Elm.Syntax.Module exposing (DefaultModuleData, EffectModuleData, Module(..)) 77 | import Elm.Syntax.ModuleName exposing (ModuleName) 78 | import Elm.Syntax.Node exposing (Node) 79 | import Elm.Syntax.Pattern exposing (Pattern(..)) 80 | import Elm.Syntax.Signature exposing (Signature) 81 | import Elm.Syntax.Type exposing (Type, ValueConstructor) 82 | import Elm.Syntax.TypeAlias exposing (TypeAlias) 83 | import Elm.Syntax.TypeAnnotation exposing (RecordField, TypeAnnotation(..)) 84 | import Hex 85 | import ImportsAndExposing 86 | import Parser exposing (Parser) 87 | import Pretty exposing (Doc) 88 | import Unicode 89 | import Util exposing (denode, denodeAll, denodeMaybe, nodify, nodifyAll) 90 | 91 | 92 | {-| Elm syntax tags. The pretty print `Doc Tag` is parameterized over this set of 93 | tags, which describe which part of the Elm syntax the various parts of the `Doc` 94 | are for. 95 | -} 96 | type Tag 97 | = KeywordTag 98 | | CommentTag 99 | | OperatorTag 100 | | TypeTag 101 | | StatementTag 102 | | SignatureTag 103 | | LiteralTag 104 | | NumberTag 105 | 106 | 107 | toToken : Tag -> String -> Doc Tag 108 | toToken t str = 109 | Pretty.taggedString str t 110 | 111 | 112 | {-| Create keywords such as `let`, `if`, `case` and so on 113 | -} 114 | keyword : String -> Doc Tag 115 | keyword = 116 | toToken KeywordTag 117 | 118 | 119 | {-| Create comments. 120 | -} 121 | comment : String -> Doc Tag 122 | comment = 123 | toToken CommentTag 124 | 125 | 126 | {-| Create operators such as `+`, `-`, `*`, `/` and so on 127 | -} 128 | operator : String -> Doc Tag 129 | operator = 130 | toToken OperatorTag 131 | 132 | 133 | {-| Create types such as `String`, `Int`, `MyType`. 134 | -} 135 | type_ : String -> Doc Tag 136 | type_ = 137 | toToken TypeTag 138 | 139 | 140 | {-| Create statements such as `map`, `update`, `view` and so on 141 | -} 142 | statement : String -> Doc Tag 143 | statement = 144 | toToken StatementTag 145 | 146 | 147 | {-| Create function signature, either top level or inside of let closure, usually followed by pattern and `=`. 148 | -} 149 | signature : String -> Doc Tag 150 | signature = 151 | toToken SignatureTag 152 | 153 | 154 | {-| Create string and char literal. 155 | -} 156 | literal : String -> Doc Tag 157 | literal = 158 | toToken LiteralTag 159 | 160 | 161 | {-| Create number literal. 162 | -} 163 | number : String -> Doc Tag 164 | number = 165 | toToken NumberTag 166 | 167 | 168 | {-| Prepares a file of Elm code for layout by the pretty printer. 169 | 170 | Note that the `Doc` type returned by this is a `Pretty.Doc`. This can be printed 171 | to a string by the `the-sett/elm-pretty-printer` package. 172 | 173 | These `Doc` based functions are exposed in case you want to pretty print some 174 | Elm inside something else with the pretty printer, or do more fancy outputs with 175 | syntax highlighting. 176 | 177 | The `pretty` function can be used to go directly from a `File` to a `String`, if 178 | that is more convenient. 179 | 180 | -} 181 | prepareLayout : Int -> File -> Doc Tag 182 | prepareLayout width file = 183 | let 184 | layoutDeclComments decls = 185 | List.map 186 | (prettyDocComment width) 187 | decls 188 | 189 | ( innerFile, tags ) = 190 | case file.comments of 191 | Just fileComment -> 192 | let 193 | ( fileCommentStr, innerTags ) = 194 | layoutFileComment width fileComment 195 | in 196 | ( { moduleDefinition = file.moduleDefinition 197 | , imports = file.imports 198 | , declarations = layoutDeclComments file.declarations |> nodifyAll 199 | , comments = nodifyAll [ fileCommentStr ] 200 | } 201 | , innerTags 202 | ) 203 | 204 | Nothing -> 205 | ( { moduleDefinition = file.moduleDefinition 206 | , imports = file.imports 207 | , declarations = layoutDeclComments file.declarations |> nodifyAll 208 | , comments = [] 209 | } 210 | , [] 211 | ) 212 | in 213 | prettyModule (denode innerFile.moduleDefinition) 214 | |> Pretty.a Pretty.line 215 | |> Pretty.a Pretty.line 216 | |> Pretty.a (prettyComments (denodeAll innerFile.comments)) 217 | |> Pretty.a (importsPretty innerFile) 218 | |> Pretty.a (prettyDeclarations (denodeAll innerFile.declarations)) 219 | 220 | 221 | importsPretty : Elm.Syntax.File.File -> Doc Tag 222 | importsPretty file = 223 | case file.imports of 224 | [] -> 225 | Pretty.line 226 | 227 | _ -> 228 | prettyImports (denodeAll file.imports) 229 | |> Pretty.a Pretty.line 230 | |> Pretty.a Pretty.line 231 | |> Pretty.a Pretty.line 232 | 233 | 234 | {-| Prints a file of Elm code to the given page width, making use of the pretty 235 | printer. 236 | -} 237 | pretty : Int -> File -> String 238 | pretty width file = 239 | prepareLayout width file 240 | |> Pretty.pretty width 241 | 242 | 243 | {-| Pretty prints a module definition. 244 | -} 245 | prettyModule : Module -> Doc Tag 246 | prettyModule mod = 247 | case mod of 248 | NormalModule defaultModuleData -> 249 | prettyDefaultModuleData defaultModuleData 250 | 251 | PortModule defaultModuleData -> 252 | prettyPortModuleData defaultModuleData 253 | 254 | EffectModule effectModuleData -> 255 | prettyEffectModuleData effectModuleData 256 | 257 | 258 | prettyModuleName : ModuleName -> Doc Tag 259 | prettyModuleName name = 260 | List.map type_ name 261 | |> Pretty.join dot 262 | 263 | 264 | prettyModuleNameDot : ModuleName -> Doc Tag 265 | prettyModuleNameDot name = 266 | case name of 267 | [] -> 268 | Pretty.empty 269 | 270 | _ -> 271 | List.map type_ name 272 | |> Pretty.join dot 273 | |> Pretty.a dot 274 | 275 | 276 | prettyModuleNameAlias : ModuleName -> Doc Tag 277 | prettyModuleNameAlias name = 278 | case name of 279 | [] -> 280 | Pretty.empty 281 | 282 | _ -> 283 | keyword "as " 284 | |> Pretty.a (List.map type_ name |> Pretty.join dot) 285 | 286 | 287 | prettyDefaultModuleData : DefaultModuleData -> Doc Tag 288 | prettyDefaultModuleData moduleData = 289 | Pretty.words 290 | [ keyword "module" 291 | , prettyModuleName (denode moduleData.moduleName) 292 | , prettyExposing (denode moduleData.exposingList) 293 | ] 294 | 295 | 296 | prettyPortModuleData : DefaultModuleData -> Doc Tag 297 | prettyPortModuleData moduleData = 298 | Pretty.words 299 | [ keyword "port module" 300 | , prettyModuleName (denode moduleData.moduleName) 301 | , prettyExposing (denode moduleData.exposingList) 302 | ] 303 | 304 | 305 | prettyEffectModuleData : EffectModuleData -> Doc Tag 306 | prettyEffectModuleData moduleData = 307 | let 308 | prettyCmdAndSub maybeCmd maybeSub = 309 | case ( maybeCmd, maybeSub ) of 310 | ( Nothing, Nothing ) -> 311 | Nothing 312 | 313 | ( Just cmdName, Just subName ) -> 314 | [ Pretty.string "where { command =" 315 | , Pretty.string cmdName 316 | , Pretty.string "," 317 | , Pretty.string "subscription =" 318 | , Pretty.string subName 319 | , Pretty.string "}" 320 | ] 321 | |> Pretty.words 322 | |> Just 323 | 324 | ( Just cmdName, Nothing ) -> 325 | [ Pretty.string "where { command =" 326 | , Pretty.string cmdName 327 | , Pretty.string "}" 328 | ] 329 | |> Pretty.words 330 | |> Just 331 | 332 | ( Nothing, Just subName ) -> 333 | [ Pretty.string "where { subscription =" 334 | , Pretty.string subName 335 | , Pretty.string "}" 336 | ] 337 | |> Pretty.words 338 | |> Just 339 | in 340 | Pretty.words 341 | [ keyword "effect module" 342 | , prettyModuleName (denode moduleData.moduleName) 343 | , prettyCmdAndSub (denodeMaybe moduleData.command) (denodeMaybe moduleData.subscription) 344 | |> prettyMaybe identity 345 | , prettyExposing (denode moduleData.exposingList) 346 | ] 347 | 348 | 349 | prettyComments : List String -> Doc Tag 350 | prettyComments comments = 351 | case comments of 352 | [] -> 353 | Pretty.empty 354 | 355 | _ -> 356 | List.foldl (\line lines -> String.split "\n" line ++ lines) [] comments 357 | |> List.map comment 358 | |> Pretty.lines 359 | |> Pretty.a Pretty.line 360 | |> Pretty.a Pretty.line 361 | 362 | 363 | {-| Pretty prints a list of import statements. 364 | 365 | The list will be de-duplicated and sorted. 366 | 367 | -} 368 | prettyImports : List Import -> Doc Tag 369 | prettyImports imports = 370 | ImportsAndExposing.sortAndDedupImports imports 371 | |> List.map prettyImport 372 | |> Pretty.lines 373 | 374 | 375 | prettyImport : Import -> Doc Tag 376 | prettyImport import_ = 377 | Pretty.join Pretty.space 378 | [ keyword "import" 379 | , prettyModuleName (denode import_.moduleName) 380 | , prettyMaybe prettyModuleNameAlias (denodeMaybe import_.moduleAlias) 381 | , prettyMaybe prettyExposing (denodeMaybe import_.exposingList) 382 | ] 383 | 384 | 385 | {-| Pretty prints the contents of an exposing statement, as found on a module or import 386 | statement. 387 | 388 | The exposed values will be de-duplicated and sorted. 389 | 390 | -} 391 | prettyExposing : Exposing -> Doc Tag 392 | prettyExposing exposing_ = 393 | let 394 | exposings = 395 | case exposing_ of 396 | All _ -> 397 | Pretty.string "(..)" 398 | 399 | Explicit tll -> 400 | ImportsAndExposing.sortAndDedupExposings (denodeAll tll) 401 | |> prettyTopLevelExposes 402 | |> Pretty.parens 403 | in 404 | keyword "exposing" 405 | |> Pretty.a Pretty.space 406 | |> Pretty.a exposings 407 | 408 | 409 | prettyTopLevelExposes : List TopLevelExpose -> Doc Tag 410 | prettyTopLevelExposes exposes = 411 | List.map prettyTopLevelExpose exposes 412 | |> Pretty.join (Pretty.string ", ") 413 | 414 | 415 | prettyTopLevelExpose : TopLevelExpose -> Doc Tag 416 | prettyTopLevelExpose tlExpose = 417 | case tlExpose of 418 | InfixExpose val -> 419 | statement val 420 | |> Pretty.parens 421 | 422 | FunctionExpose val -> 423 | signature val 424 | 425 | TypeOrAliasExpose val -> 426 | type_ val 427 | 428 | TypeExpose exposedType -> 429 | case exposedType.open of 430 | Nothing -> 431 | type_ exposedType.name 432 | 433 | Just _ -> 434 | type_ exposedType.name 435 | |> Pretty.a (Pretty.string "(..)") 436 | 437 | 438 | 439 | --== Declarations 440 | 441 | 442 | {-| Pretty prints a single top-level declaration. 443 | -} 444 | prettyDeclaration : Int -> Declaration -> Doc Tag 445 | prettyDeclaration width decl = 446 | let 447 | innerDecl = 448 | prettyDocComment width decl 449 | in 450 | prettyElmSyntaxDeclaration innerDecl 451 | 452 | 453 | {-| Pretty prints an elm-syntax declaration. 454 | -} 455 | prettyElmSyntaxDeclaration : Elm.Syntax.Declaration.Declaration -> Doc Tag 456 | prettyElmSyntaxDeclaration decl = 457 | case decl of 458 | Elm.Syntax.Declaration.FunctionDeclaration fn -> 459 | prettyFun fn 460 | 461 | Elm.Syntax.Declaration.AliasDeclaration tAlias -> 462 | prettyTypeAlias tAlias 463 | 464 | Elm.Syntax.Declaration.CustomTypeDeclaration elmType -> 465 | prettyCustomType elmType 466 | 467 | Elm.Syntax.Declaration.PortDeclaration sig -> 468 | prettyPortDeclaration sig 469 | 470 | Elm.Syntax.Declaration.InfixDeclaration infix_ -> 471 | prettyInfix infix_ 472 | 473 | Elm.Syntax.Declaration.Destructuring pattern expr -> 474 | prettyDestructuring (denode pattern) (denode expr) 475 | 476 | 477 | prettyDeclarations : List Elm.Syntax.Declaration.Declaration -> Doc Tag 478 | prettyDeclarations decls = 479 | List.map 480 | (\decl -> 481 | prettyElmSyntaxDeclaration decl 482 | |> Pretty.a Pretty.line 483 | ) 484 | decls 485 | |> doubleLines 486 | 487 | 488 | {-| Pretty prints any doc comments on a declaration to string format, and provides 489 | the result as an elm-syntax declaration. 490 | -} 491 | prettyDocComment : Int -> Declaration -> Elm.Syntax.Declaration.Declaration 492 | prettyDocComment width decl = 493 | case decl of 494 | DeclWithComment declComment declFn -> 495 | declFn (layoutDocComment width declComment) 496 | 497 | DeclNoComment declNoComment -> 498 | declNoComment 499 | 500 | 501 | {-| Pretty prints an Elm function, which may include documentation and a signature too. 502 | -} 503 | prettyFun : Function -> Doc Tag 504 | prettyFun fn = 505 | [ prettyMaybe prettyDocumentation (denodeMaybe fn.documentation) 506 | , prettyMaybe prettySignature (denodeMaybe fn.signature) 507 | , prettyFunctionImplementation (denode fn.declaration) 508 | ] 509 | |> Pretty.lines 510 | 511 | 512 | {-| Pretty prints a type alias definition, which may include documentation too. 513 | -} 514 | prettyTypeAlias : TypeAlias -> Doc Tag 515 | prettyTypeAlias tAlias = 516 | let 517 | typeAliasPretty = 518 | [ keyword "type alias" 519 | , type_ (denode tAlias.name) 520 | , List.map statement (denodeAll tAlias.generics) |> Pretty.words 521 | , Pretty.string "=" 522 | ] 523 | |> Pretty.words 524 | |> Pretty.a Pretty.line 525 | |> Pretty.a (prettyTypeAnnotation (denode tAlias.typeAnnotation)) 526 | |> Pretty.nest 4 527 | in 528 | [ prettyMaybe prettyDocumentation (denodeMaybe tAlias.documentation) 529 | , typeAliasPretty 530 | ] 531 | |> Pretty.lines 532 | 533 | 534 | {-| Pretty prints a custom type declaration, which may include documentation too. 535 | -} 536 | prettyCustomType : Type -> Doc Tag 537 | prettyCustomType elmType = 538 | let 539 | customTypePretty = 540 | [ keyword "type" 541 | , type_ (denode elmType.name) 542 | , List.map statement (denodeAll elmType.generics) |> Pretty.words 543 | ] 544 | |> Pretty.words 545 | |> Pretty.a Pretty.line 546 | |> Pretty.a (Pretty.string "= ") 547 | |> Pretty.a (prettyValueConstructors (denodeAll elmType.constructors)) 548 | |> Pretty.nest 4 549 | in 550 | [ prettyMaybe prettyDocumentation (denodeMaybe elmType.documentation) 551 | , customTypePretty 552 | ] 553 | |> Pretty.lines 554 | 555 | 556 | prettyValueConstructors : List ValueConstructor -> Doc Tag 557 | prettyValueConstructors constructors = 558 | List.map prettyValueConstructor constructors 559 | |> Pretty.join (Pretty.line |> Pretty.a (Pretty.string "| ")) 560 | 561 | 562 | prettyValueConstructor : ValueConstructor -> Doc Tag 563 | prettyValueConstructor cons = 564 | [ type_ (denode cons.name) 565 | , List.map prettyTypeAnnotationParens (denodeAll cons.arguments) |> Pretty.lines 566 | ] 567 | |> Pretty.lines 568 | |> Pretty.group 569 | |> Pretty.nest 4 570 | 571 | 572 | {-| Pretty prints a port declaration. 573 | -} 574 | prettyPortDeclaration : Signature -> Doc Tag 575 | prettyPortDeclaration sig = 576 | [ keyword "port" 577 | , prettySignature sig 578 | ] 579 | |> Pretty.words 580 | 581 | 582 | prettyInfix : Infix -> Doc Tag 583 | prettyInfix infix_ = 584 | let 585 | dirToString direction = 586 | case direction of 587 | Left -> 588 | "left" 589 | 590 | Right -> 591 | "right" 592 | 593 | Non -> 594 | "non" 595 | in 596 | [ signature "infix" 597 | , statement (dirToString (denode infix_.direction)) 598 | , number (String.fromInt (denode infix_.precedence)) 599 | , statement (denode infix_.operator) |> Pretty.parens 600 | , Pretty.string "=" 601 | , statement (denode infix_.function) 602 | ] 603 | |> Pretty.words 604 | 605 | 606 | {-| Pretty prints a destructuring declaration. 607 | -} 608 | prettyDestructuring : Pattern -> Expression -> Doc Tag 609 | prettyDestructuring pattern expr = 610 | [ [ prettyPattern pattern 611 | , Pretty.string "=" 612 | ] 613 | |> Pretty.words 614 | , prettyExpression expr 615 | ] 616 | |> Pretty.lines 617 | |> Pretty.nest 4 618 | 619 | 620 | prettyDocumentation : Documentation -> Doc Tag 621 | prettyDocumentation docs = 622 | comment docs 623 | 624 | 625 | {-| Pretty prints a type signature. 626 | -} 627 | prettySignature : Signature -> Doc Tag 628 | prettySignature sig = 629 | [ [ signature (denode sig.name) 630 | , Pretty.string ":" 631 | ] 632 | |> Pretty.words 633 | , prettyTypeAnnotation (denode sig.typeAnnotation) 634 | ] 635 | |> Pretty.lines 636 | |> Pretty.nest 4 637 | |> Pretty.group 638 | 639 | 640 | prettyFunctionImplementation : FunctionImplementation -> Doc Tag 641 | prettyFunctionImplementation impl = 642 | Pretty.words 643 | [ signature (denode impl.name) 644 | , prettyArgs (denodeAll impl.arguments) 645 | , Pretty.string "=" 646 | ] 647 | |> Pretty.a Pretty.line 648 | |> Pretty.a (prettyExpression (denode impl.expression)) 649 | |> Pretty.nest 4 650 | 651 | 652 | prettyArgs : List Pattern -> Doc Tag 653 | prettyArgs args = 654 | List.map (prettyPatternInner False) args 655 | |> Pretty.words 656 | 657 | 658 | 659 | --== Patterns 660 | 661 | 662 | {-| Pretty prints a pattern. 663 | -} 664 | prettyPattern : Pattern -> Doc Tag 665 | prettyPattern pattern = 666 | prettyPatternInner True pattern 667 | 668 | 669 | adjustPatternParentheses : Bool -> Pattern -> Pattern 670 | adjustPatternParentheses isTop pattern = 671 | let 672 | addParens pat = 673 | case ( isTop, pat ) of 674 | ( False, NamedPattern _ (_ :: _) ) -> 675 | nodify pat |> ParenthesizedPattern 676 | 677 | ( False, AsPattern _ _ ) -> 678 | nodify pat |> ParenthesizedPattern 679 | 680 | ( _, _ ) -> 681 | pat 682 | 683 | removeParens pat = 684 | case pat of 685 | ParenthesizedPattern innerPat -> 686 | if shouldRemove (denode innerPat) then 687 | denode innerPat 688 | |> removeParens 689 | 690 | else 691 | pat 692 | 693 | _ -> 694 | pat 695 | 696 | shouldRemove pat = 697 | case ( isTop, pat ) of 698 | ( False, NamedPattern _ _ ) -> 699 | False 700 | 701 | ( _, AsPattern _ _ ) -> 702 | False 703 | 704 | ( _, _ ) -> 705 | isTop 706 | in 707 | removeParens pattern 708 | |> addParens 709 | 710 | 711 | prettyPatternInner : Bool -> Pattern -> Doc Tag 712 | prettyPatternInner isTop pattern = 713 | case adjustPatternParentheses isTop pattern of 714 | AllPattern -> 715 | statement "_" 716 | 717 | UnitPattern -> 718 | statement "()" 719 | 720 | CharPattern val -> 721 | literal (escapeChar val) 722 | |> singleQuotes 723 | 724 | StringPattern val -> 725 | literal val 726 | |> quotes 727 | 728 | IntPattern val -> 729 | number (String.fromInt val) 730 | 731 | HexPattern val -> 732 | number (toHexString val) 733 | 734 | FloatPattern val -> 735 | number (String.fromFloat val) 736 | 737 | TuplePattern vals -> 738 | Pretty.space 739 | |> Pretty.a 740 | (List.map (prettyPatternInner True) (denodeAll vals) 741 | |> Pretty.join (Pretty.string ", ") 742 | ) 743 | |> Pretty.a Pretty.space 744 | |> Pretty.parens 745 | 746 | RecordPattern fields -> 747 | List.map statement (denodeAll fields) 748 | |> Pretty.join (Pretty.string ", ") 749 | |> Pretty.surround Pretty.space Pretty.space 750 | |> Pretty.braces 751 | 752 | UnConsPattern hdPat tlPat -> 753 | [ prettyPatternInner False (denode hdPat) 754 | , operator "::" 755 | , prettyPatternInner False (denode tlPat) 756 | ] 757 | |> Pretty.words 758 | 759 | ListPattern listPats -> 760 | case listPats of 761 | [] -> 762 | Pretty.string "[]" 763 | 764 | _ -> 765 | let 766 | open = 767 | Pretty.a Pretty.space (Pretty.string "[") 768 | 769 | close = 770 | Pretty.a (Pretty.string "]") Pretty.space 771 | in 772 | List.map (prettyPatternInner False) (denodeAll listPats) 773 | |> Pretty.join (Pretty.string ", ") 774 | |> Pretty.surround open close 775 | 776 | VarPattern var -> 777 | statement var 778 | 779 | NamedPattern qnRef listPats -> 780 | (prettyModuleNameDot qnRef.moduleName 781 | |> Pretty.a (type_ qnRef.name) 782 | ) 783 | :: List.map (prettyPatternInner False) (denodeAll listPats) 784 | |> Pretty.words 785 | 786 | AsPattern pat name -> 787 | [ prettyPatternInner False (denode pat) 788 | , keyword "as" 789 | , statement (denode name) 790 | ] 791 | |> Pretty.words 792 | 793 | ParenthesizedPattern pat -> 794 | prettyPatternInner True (denode pat) 795 | |> Pretty.parens 796 | 797 | 798 | 799 | --== Expressions 800 | 801 | 802 | type alias Context = 803 | { precedence : Int 804 | , isTop : Bool 805 | , isLeftPipe : Bool 806 | } 807 | 808 | 809 | topContext = 810 | { precedence = 11 811 | , isTop = True 812 | , isLeftPipe = False 813 | } 814 | 815 | 816 | adjustExpressionParentheses : Context -> Expression -> Expression 817 | adjustExpressionParentheses context expression = 818 | let 819 | addParens expr = 820 | case ( context.isTop, context.isLeftPipe, expr ) of 821 | ( False, False, LetExpression _ ) -> 822 | nodify expr |> ParenthesizedExpression 823 | 824 | ( False, False, CaseExpression _ ) -> 825 | nodify expr |> ParenthesizedExpression 826 | 827 | ( False, False, LambdaExpression _ ) -> 828 | nodify expr |> ParenthesizedExpression 829 | 830 | ( False, False, IfBlock _ _ _ ) -> 831 | nodify expr |> ParenthesizedExpression 832 | 833 | ( _, _, _ ) -> 834 | expr 835 | 836 | removeParens expr = 837 | case expr of 838 | ParenthesizedExpression innerExpr -> 839 | if shouldRemove (denode innerExpr) then 840 | denode innerExpr 841 | |> removeParens 842 | 843 | else 844 | expr 845 | 846 | _ -> 847 | expr 848 | 849 | shouldRemove expr = 850 | case ( context.isTop, context.isLeftPipe, expr ) of 851 | ( True, _, _ ) -> 852 | True 853 | 854 | ( _, True, _ ) -> 855 | True 856 | 857 | ( False, _, Application _ ) -> 858 | if context.precedence < 11 then 859 | True 860 | 861 | else 862 | False 863 | 864 | ( False, _, FunctionOrValue _ _ ) -> 865 | True 866 | 867 | ( False, _, Integer _ ) -> 868 | True 869 | 870 | ( False, _, Hex _ ) -> 871 | True 872 | 873 | ( False, _, Floatable _ ) -> 874 | True 875 | 876 | ( False, _, Negation _ ) -> 877 | True 878 | 879 | ( False, _, Literal _ ) -> 880 | True 881 | 882 | ( False, _, CharLiteral _ ) -> 883 | True 884 | 885 | ( False, _, TupledExpression _ ) -> 886 | True 887 | 888 | ( False, _, RecordExpr _ ) -> 889 | True 890 | 891 | ( False, _, ListExpr _ ) -> 892 | True 893 | 894 | ( False, _, RecordAccess _ _ ) -> 895 | True 896 | 897 | ( False, _, RecordAccessFunction _ ) -> 898 | True 899 | 900 | ( False, _, RecordUpdateExpression _ _ ) -> 901 | True 902 | 903 | ( _, _, _ ) -> 904 | False 905 | in 906 | removeParens expression 907 | |> addParens 908 | 909 | 910 | {-| Pretty prints an expression. 911 | -} 912 | prettyExpression : Expression -> Doc Tag 913 | prettyExpression expression = 914 | prettyExpressionInner topContext 4 expression 915 | |> Tuple.first 916 | 917 | 918 | prettyFunctionOrValue : ModuleName -> String -> ( Doc Tag, Bool ) 919 | prettyFunctionOrValue modl val = 920 | let 921 | token = 922 | case String.uncons val of 923 | Just ( c, _ ) -> 924 | if Char.isUpper c then 925 | type_ val 926 | 927 | else 928 | statement val 929 | 930 | Nothing -> 931 | statement val 932 | in 933 | ( prettyModuleNameDot modl 934 | |> Pretty.a token 935 | , False 936 | ) 937 | 938 | 939 | prettyExpressionInner : Context -> Int -> Expression -> ( Doc Tag, Bool ) 940 | prettyExpressionInner context indent expression = 941 | case adjustExpressionParentheses context expression of 942 | UnitExpr -> 943 | ( statement "()" 944 | , False 945 | ) 946 | 947 | Application exprs -> 948 | prettyApplication indent exprs 949 | 950 | OperatorApplication symbol dir exprl exprr -> 951 | prettyOperatorApplication indent symbol dir exprl exprr 952 | 953 | FunctionOrValue modl val -> 954 | prettyFunctionOrValue modl val 955 | 956 | IfBlock exprBool exprTrue exprFalse -> 957 | prettyIfBlock indent exprBool exprTrue exprFalse 958 | 959 | PrefixOperator symbol -> 960 | ( statement symbol |> Pretty.parens 961 | , False 962 | ) 963 | 964 | Operator symbol -> 965 | ( operator symbol 966 | , False 967 | ) 968 | 969 | Integer val -> 970 | ( number (String.fromInt val) 971 | , False 972 | ) 973 | 974 | Hex val -> 975 | ( number (toHexString val) 976 | , False 977 | ) 978 | 979 | Floatable val -> 980 | ( number (String.fromFloat val) 981 | , False 982 | ) 983 | 984 | Negation expr -> 985 | let 986 | ( prettyExpr, alwaysBreak ) = 987 | prettyExpressionInner topContext 4 (denode expr) 988 | in 989 | ( statement "-" 990 | |> Pretty.a prettyExpr 991 | , alwaysBreak 992 | ) 993 | 994 | Literal val -> 995 | ( prettyLiteral val 996 | , False 997 | ) 998 | 999 | CharLiteral val -> 1000 | ( literal (escapeChar val) 1001 | |> singleQuotes 1002 | , False 1003 | ) 1004 | 1005 | TupledExpression exprs -> 1006 | prettyTupledExpression indent exprs 1007 | 1008 | ParenthesizedExpression expr -> 1009 | prettyParenthesizedExpression indent expr 1010 | 1011 | LetExpression letBlock -> 1012 | prettyLetBlock indent letBlock 1013 | 1014 | CaseExpression caseBlock -> 1015 | prettyCaseBlock indent caseBlock 1016 | 1017 | LambdaExpression lambda -> 1018 | prettyLambdaExpression indent lambda 1019 | 1020 | RecordExpr setters -> 1021 | prettyRecordExpr setters 1022 | 1023 | ListExpr exprs -> 1024 | prettyList indent exprs 1025 | 1026 | RecordAccess expr field -> 1027 | prettyRecordAccess expr field 1028 | 1029 | RecordAccessFunction field -> 1030 | ( statement field 1031 | , False 1032 | ) 1033 | 1034 | RecordUpdateExpression var setters -> 1035 | prettyRecordUpdateExpression indent var setters 1036 | 1037 | GLSLExpression val -> 1038 | ( statement "glsl" 1039 | , True 1040 | ) 1041 | 1042 | 1043 | prettyApplication : Int -> List (Node Expression) -> ( Doc Tag, Bool ) 1044 | prettyApplication indent exprs = 1045 | let 1046 | ( prettyExpressions, alwaysBreak ) = 1047 | List.map (prettyExpressionInner { precedence = 11, isTop = False, isLeftPipe = False } 4) (denodeAll exprs) 1048 | |> List.unzip 1049 | |> Tuple.mapSecond Bool.Extra.any 1050 | in 1051 | ( prettyExpressions 1052 | |> Pretty.lines 1053 | |> Pretty.nest indent 1054 | |> Pretty.align 1055 | |> optionalGroup alwaysBreak 1056 | , alwaysBreak 1057 | ) 1058 | 1059 | 1060 | isEndLineOperator : String -> Bool 1061 | isEndLineOperator op = 1062 | case op of 1063 | "<|" -> 1064 | True 1065 | 1066 | _ -> 1067 | False 1068 | 1069 | 1070 | prettyOperatorApplication : Int -> String -> InfixDirection -> Node Expression -> Node Expression -> ( Doc Tag, Bool ) 1071 | prettyOperatorApplication indent symbol dir exprl exprr = 1072 | if symbol == "<|" then 1073 | prettyOperatorApplicationLeft indent symbol dir exprl exprr 1074 | 1075 | else 1076 | prettyOperatorApplicationRight indent symbol dir exprl exprr 1077 | 1078 | 1079 | prettyOperatorApplicationLeft : Int -> String -> InfixDirection -> Node Expression -> Node Expression -> ( Doc Tag, Bool ) 1080 | prettyOperatorApplicationLeft indent symbol _ exprl exprr = 1081 | let 1082 | context = 1083 | { precedence = precedence symbol 1084 | , isTop = False 1085 | , isLeftPipe = True 1086 | } 1087 | 1088 | ( prettyExpressionLeft, alwaysBreakLeft ) = 1089 | prettyExpressionInner context 4 (denode exprl) 1090 | 1091 | ( prettyExpressionRight, alwaysBreakRight ) = 1092 | prettyExpressionInner context 4 (denode exprr) 1093 | 1094 | alwaysBreak = 1095 | alwaysBreakLeft || alwaysBreakRight 1096 | in 1097 | ( [ [ prettyExpressionLeft, operator symbol ] |> Pretty.words 1098 | , prettyExpressionRight 1099 | ] 1100 | |> Pretty.lines 1101 | |> optionalGroup alwaysBreak 1102 | |> Pretty.nest 4 1103 | , alwaysBreak 1104 | ) 1105 | 1106 | 1107 | prettyOperatorApplicationRight : Int -> String -> InfixDirection -> Node Expression -> Node Expression -> ( Doc Tag, Bool ) 1108 | prettyOperatorApplicationRight indent symbol _ exprl exprr = 1109 | let 1110 | expandExpr : Int -> Context -> Expression -> List ( Doc Tag, Bool ) 1111 | expandExpr innerIndent context expr = 1112 | case expr of 1113 | OperatorApplication sym _ left right -> 1114 | innerOpApply False sym left right 1115 | 1116 | _ -> 1117 | [ prettyExpressionInner context innerIndent expr ] 1118 | 1119 | innerOpApply : Bool -> String -> Node Expression -> Node Expression -> List ( Doc Tag, Bool ) 1120 | innerOpApply isTop sym left right = 1121 | let 1122 | context = 1123 | { precedence = precedence sym 1124 | , isTop = False 1125 | , isLeftPipe = "<|" == sym 1126 | } 1127 | 1128 | innerIndent = 1129 | decrementIndent 4 (String.length symbol + 1) 1130 | 1131 | leftIndent = 1132 | if isTop then 1133 | indent 1134 | 1135 | else 1136 | innerIndent 1137 | 1138 | rightSide = 1139 | denode right |> expandExpr innerIndent context 1140 | in 1141 | case rightSide of 1142 | ( hdExpr, hdBreak ) :: tl -> 1143 | List.append (denode left |> expandExpr leftIndent context) 1144 | (( operator sym |> Pretty.a Pretty.space |> Pretty.a hdExpr, hdBreak ) :: tl) 1145 | 1146 | [] -> 1147 | [] 1148 | 1149 | ( prettyExpressions, alwaysBreak ) = 1150 | innerOpApply True symbol exprl exprr 1151 | |> List.unzip 1152 | |> Tuple.mapSecond Bool.Extra.any 1153 | in 1154 | ( prettyExpressions 1155 | |> Pretty.join (Pretty.nest indent Pretty.line) 1156 | |> Pretty.align 1157 | |> optionalGroup alwaysBreak 1158 | , alwaysBreak 1159 | ) 1160 | 1161 | 1162 | prettyIfBlock : Int -> Node Expression -> Node Expression -> Node Expression -> ( Doc Tag, Bool ) 1163 | prettyIfBlock indent exprBool exprTrue exprFalse = 1164 | let 1165 | innerIfBlock : Node Expression -> Node Expression -> Node Expression -> List (Doc Tag) 1166 | innerIfBlock innerExprBool innerExprTrue innerExprFalse = 1167 | let 1168 | context = 1169 | topContext 1170 | 1171 | ifPart = 1172 | let 1173 | ( prettyBoolExpr, alwaysBreak ) = 1174 | prettyExpressionInner topContext 4 (denode innerExprBool) 1175 | in 1176 | [ [ keyword "if" 1177 | , prettyExpressionInner topContext 4 (denode innerExprBool) |> Tuple.first 1178 | ] 1179 | |> Pretty.lines 1180 | |> optionalGroup alwaysBreak 1181 | |> Pretty.nest indent 1182 | , keyword "then" 1183 | ] 1184 | |> Pretty.lines 1185 | |> optionalGroup alwaysBreak 1186 | 1187 | truePart = 1188 | prettyExpressionInner topContext 4 (denode innerExprTrue) 1189 | |> Tuple.first 1190 | |> Pretty.indent indent 1191 | 1192 | elsePart = 1193 | Pretty.line 1194 | |> Pretty.a (keyword "else") 1195 | 1196 | falsePart = 1197 | case denode innerExprFalse of 1198 | IfBlock nestedExprBool nestedExprTrue nestedExprFalse -> 1199 | innerIfBlock nestedExprBool nestedExprTrue nestedExprFalse 1200 | 1201 | _ -> 1202 | [ prettyExpressionInner topContext 4 (denode innerExprFalse) 1203 | |> Tuple.first 1204 | |> Pretty.indent indent 1205 | ] 1206 | in 1207 | case falsePart of 1208 | [] -> 1209 | [] 1210 | 1211 | [ falseExpr ] -> 1212 | [ ifPart 1213 | , truePart 1214 | , elsePart 1215 | , falseExpr 1216 | ] 1217 | 1218 | hd :: tl -> 1219 | List.append 1220 | [ ifPart 1221 | , truePart 1222 | , [ elsePart, hd ] |> Pretty.words 1223 | ] 1224 | tl 1225 | 1226 | prettyExpressions = 1227 | innerIfBlock exprBool exprTrue exprFalse 1228 | in 1229 | ( prettyExpressions 1230 | |> Pretty.lines 1231 | |> Pretty.align 1232 | , True 1233 | ) 1234 | 1235 | 1236 | prettyLiteral : String -> Doc Tag 1237 | prettyLiteral val = 1238 | literal (escape val) 1239 | |> quotes 1240 | 1241 | 1242 | prettyTupledExpression : Int -> List (Node Expression) -> ( Doc Tag, Bool ) 1243 | prettyTupledExpression indent exprs = 1244 | let 1245 | open = 1246 | Pretty.a Pretty.space (Pretty.string "(") 1247 | 1248 | close = 1249 | Pretty.a (Pretty.string ")") Pretty.line 1250 | in 1251 | case exprs of 1252 | [] -> 1253 | ( Pretty.string "()", False ) 1254 | 1255 | _ -> 1256 | let 1257 | ( prettyExpressions, alwaysBreak ) = 1258 | List.map (prettyExpressionInner topContext (decrementIndent indent 2)) (denodeAll exprs) 1259 | |> List.unzip 1260 | |> Tuple.mapSecond Bool.Extra.any 1261 | in 1262 | ( prettyExpressions 1263 | |> Pretty.separators ", " 1264 | |> Pretty.surround open close 1265 | |> Pretty.align 1266 | |> optionalGroup alwaysBreak 1267 | , alwaysBreak 1268 | ) 1269 | 1270 | 1271 | prettyParenthesizedExpression : Int -> Node Expression -> ( Doc Tag, Bool ) 1272 | prettyParenthesizedExpression indent expr = 1273 | let 1274 | open = 1275 | Pretty.string "(" 1276 | 1277 | close = 1278 | Pretty.a (Pretty.string ")") Pretty.tightline 1279 | 1280 | ( prettyExpr, alwaysBreak ) = 1281 | prettyExpressionInner topContext (decrementIndent indent 1) (denode expr) 1282 | in 1283 | ( prettyExpr 1284 | |> Pretty.nest 1 1285 | |> Pretty.surround open close 1286 | |> Pretty.align 1287 | |> optionalGroup alwaysBreak 1288 | , alwaysBreak 1289 | ) 1290 | 1291 | 1292 | prettyLetBlock : Int -> LetBlock -> ( Doc Tag, Bool ) 1293 | prettyLetBlock indent letBlock = 1294 | ( [ keyword "let" 1295 | , List.map (prettyLetDeclaration indent) (denodeAll letBlock.declarations) 1296 | |> doubleLines 1297 | |> Pretty.indent indent 1298 | , keyword "in" 1299 | , prettyExpressionInner topContext 4 (denode letBlock.expression) |> Tuple.first 1300 | ] 1301 | |> Pretty.lines 1302 | |> Pretty.align 1303 | , True 1304 | ) 1305 | 1306 | 1307 | prettyLetDeclaration : Int -> LetDeclaration -> Doc Tag 1308 | prettyLetDeclaration indent letDecl = 1309 | case letDecl of 1310 | LetFunction fn -> 1311 | prettyFun fn 1312 | 1313 | LetDestructuring pattern expr -> 1314 | [ prettyPatternInner False (denode pattern) 1315 | , Pretty.string "=" 1316 | ] 1317 | |> Pretty.words 1318 | |> Pretty.a Pretty.line 1319 | |> Pretty.a 1320 | (prettyExpressionInner topContext 4 (denode expr) 1321 | |> Tuple.first 1322 | |> Pretty.indent indent 1323 | ) 1324 | 1325 | 1326 | prettyCaseBlock : Int -> CaseBlock -> ( Doc Tag, Bool ) 1327 | prettyCaseBlock indent caseBlock = 1328 | let 1329 | casePart = 1330 | let 1331 | ( caseExpression, alwaysBreak ) = 1332 | prettyExpressionInner topContext 4 (denode caseBlock.expression) 1333 | in 1334 | [ [ keyword "case" 1335 | , caseExpression 1336 | ] 1337 | |> Pretty.lines 1338 | |> optionalGroup alwaysBreak 1339 | |> Pretty.nest indent 1340 | , keyword "of" 1341 | ] 1342 | |> Pretty.lines 1343 | |> optionalGroup alwaysBreak 1344 | 1345 | prettyCase ( pattern, expr ) = 1346 | prettyPattern (denode pattern) 1347 | |> Pretty.a (Pretty.string " ->") 1348 | |> Pretty.a Pretty.line 1349 | |> Pretty.a (prettyExpressionInner topContext 4 (denode expr) |> Tuple.first |> Pretty.indent 4) 1350 | |> Pretty.indent indent 1351 | 1352 | patternsPart = 1353 | List.map prettyCase caseBlock.cases 1354 | |> doubleLines 1355 | in 1356 | ( [ casePart, patternsPart ] 1357 | |> Pretty.lines 1358 | |> Pretty.align 1359 | , True 1360 | ) 1361 | 1362 | 1363 | prettyLambdaExpression : Int -> Lambda -> ( Doc Tag, Bool ) 1364 | prettyLambdaExpression indent lambda = 1365 | let 1366 | ( prettyExpr, alwaysBreak ) = 1367 | prettyExpressionInner topContext 4 (denode lambda.expression) 1368 | in 1369 | ( [ Pretty.string "\\" 1370 | |> Pretty.a (List.map (prettyPatternInner False) (denodeAll lambda.args) |> Pretty.words) 1371 | |> Pretty.a (Pretty.string " ->") 1372 | , prettyExpr 1373 | ] 1374 | |> Pretty.lines 1375 | |> Pretty.nest indent 1376 | |> Pretty.align 1377 | |> optionalGroup alwaysBreak 1378 | , alwaysBreak 1379 | ) 1380 | 1381 | 1382 | prettyRecordExpr : List (Node RecordSetter) -> ( Doc Tag, Bool ) 1383 | prettyRecordExpr setters = 1384 | let 1385 | open = 1386 | Pretty.a Pretty.space (Pretty.string "{") 1387 | 1388 | close = 1389 | Pretty.a (Pretty.string "}") 1390 | Pretty.line 1391 | in 1392 | case setters of 1393 | [] -> 1394 | ( Pretty.string "{}", False ) 1395 | 1396 | _ -> 1397 | let 1398 | ( prettyExpressions, alwaysBreak ) = 1399 | List.map prettySetter (denodeAll setters) 1400 | |> List.unzip 1401 | |> Tuple.mapSecond Bool.Extra.any 1402 | in 1403 | ( prettyExpressions 1404 | |> Pretty.separators ", " 1405 | |> Pretty.surround open close 1406 | |> Pretty.align 1407 | |> optionalGroup alwaysBreak 1408 | , alwaysBreak 1409 | ) 1410 | 1411 | 1412 | prettySetter : ( Node String, Node Expression ) -> ( Doc Tag, Bool ) 1413 | prettySetter ( fld, val ) = 1414 | let 1415 | ( prettyExpr, alwaysBreak ) = 1416 | prettyExpressionInner topContext 4 (denode val) 1417 | in 1418 | ( [ [ statement (denode fld) 1419 | , Pretty.string "=" 1420 | ] 1421 | |> Pretty.words 1422 | , prettyExpr 1423 | ] 1424 | |> Pretty.lines 1425 | |> optionalGroup alwaysBreak 1426 | |> Pretty.nest 4 1427 | , alwaysBreak 1428 | ) 1429 | 1430 | 1431 | prettyList : Int -> List (Node Expression) -> ( Doc Tag, Bool ) 1432 | prettyList indent exprs = 1433 | let 1434 | open = 1435 | Pretty.a Pretty.space (Pretty.string "[") 1436 | 1437 | close = 1438 | Pretty.a (Pretty.string "]") Pretty.line 1439 | in 1440 | case exprs of 1441 | [] -> 1442 | ( Pretty.string "[]", False ) 1443 | 1444 | _ -> 1445 | let 1446 | ( prettyExpressions, alwaysBreak ) = 1447 | List.map (prettyExpressionInner topContext (decrementIndent indent 2)) (denodeAll exprs) 1448 | |> List.unzip 1449 | |> Tuple.mapSecond Bool.Extra.any 1450 | in 1451 | ( prettyExpressions 1452 | |> Pretty.separators ", " 1453 | |> Pretty.surround open close 1454 | |> Pretty.align 1455 | |> optionalGroup alwaysBreak 1456 | , alwaysBreak 1457 | ) 1458 | 1459 | 1460 | prettyRecordAccess : Node Expression -> Node String -> ( Doc Tag, Bool ) 1461 | prettyRecordAccess expr field = 1462 | let 1463 | ( prettyExpr, alwaysBreak ) = 1464 | prettyExpressionInner topContext 4 (denode expr) 1465 | in 1466 | ( prettyExpr 1467 | |> Pretty.a dot 1468 | |> Pretty.a (statement (denode field)) 1469 | , alwaysBreak 1470 | ) 1471 | 1472 | 1473 | prettyRecordUpdateExpression : Int -> Node String -> List (Node RecordSetter) -> ( Doc Tag, Bool ) 1474 | prettyRecordUpdateExpression indent var setters = 1475 | let 1476 | open = 1477 | [ Pretty.string "{" 1478 | , statement (denode var) 1479 | ] 1480 | |> Pretty.words 1481 | |> Pretty.a Pretty.line 1482 | 1483 | close = 1484 | Pretty.a (Pretty.string "}") 1485 | Pretty.line 1486 | 1487 | addBarToFirst exprs = 1488 | case exprs of 1489 | [] -> 1490 | [] 1491 | 1492 | hd :: tl -> 1493 | Pretty.a hd (Pretty.string "| ") :: tl 1494 | in 1495 | case setters of 1496 | [] -> 1497 | ( Pretty.string "{}", False ) 1498 | 1499 | _ -> 1500 | let 1501 | ( prettyExpressions, alwaysBreak ) = 1502 | List.map prettySetter (denodeAll setters) 1503 | |> List.unzip 1504 | |> Tuple.mapSecond Bool.Extra.any 1505 | in 1506 | ( open 1507 | |> Pretty.a 1508 | (prettyExpressions 1509 | |> addBarToFirst 1510 | |> Pretty.separators ", " 1511 | ) 1512 | |> Pretty.nest indent 1513 | |> Pretty.surround Pretty.empty close 1514 | |> Pretty.align 1515 | |> optionalGroup alwaysBreak 1516 | , alwaysBreak 1517 | ) 1518 | 1519 | 1520 | 1521 | --== Type Annotations 1522 | 1523 | 1524 | {-| Pretty prints a type annotation. 1525 | -} 1526 | prettyTypeAnnotation : TypeAnnotation -> Doc Tag 1527 | prettyTypeAnnotation typeAnn = 1528 | case typeAnn of 1529 | GenericType val -> 1530 | statement val 1531 | 1532 | Typed fqName anns -> 1533 | prettyTyped fqName anns 1534 | 1535 | Unit -> 1536 | statement "()" 1537 | 1538 | Tupled anns -> 1539 | prettyTupled anns 1540 | 1541 | Record recordDef -> 1542 | prettyRecord (denodeAll recordDef) 1543 | 1544 | GenericRecord paramName recordDef -> 1545 | prettyGenericRecord (denode paramName) (denodeAll (denode recordDef)) 1546 | 1547 | FunctionTypeAnnotation fromAnn toAnn -> 1548 | prettyFunctionTypeAnnotation fromAnn toAnn 1549 | 1550 | 1551 | prettyTyped : Node ( ModuleName, String ) -> List (Node TypeAnnotation) -> Doc Tag 1552 | prettyTyped fqName anns = 1553 | let 1554 | ( moduleName, typeName ) = 1555 | denode fqName 1556 | 1557 | typeDoc = 1558 | prettyModuleNameDot moduleName 1559 | |> Pretty.a (type_ typeName) 1560 | 1561 | argsDoc = 1562 | List.map prettyTypeAnnotationParens (denodeAll anns) 1563 | |> Pretty.words 1564 | in 1565 | [ typeDoc 1566 | , argsDoc 1567 | ] 1568 | |> Pretty.words 1569 | 1570 | 1571 | prettyTupled : List (Node TypeAnnotation) -> Doc Tag 1572 | prettyTupled anns = 1573 | Pretty.space 1574 | |> Pretty.a 1575 | (List.map prettyTypeAnnotation (denodeAll anns) 1576 | |> Pretty.join (Pretty.string ", ") 1577 | ) 1578 | |> Pretty.a Pretty.space 1579 | |> Pretty.parens 1580 | 1581 | 1582 | prettyTypeAnnotationParens : TypeAnnotation -> Doc Tag 1583 | prettyTypeAnnotationParens typeAnn = 1584 | if isNakedCompound typeAnn then 1585 | prettyTypeAnnotation typeAnn |> Pretty.parens 1586 | 1587 | else 1588 | prettyTypeAnnotation typeAnn 1589 | 1590 | 1591 | prettyRecord : List RecordField -> Doc Tag 1592 | prettyRecord fields = 1593 | let 1594 | open = 1595 | Pretty.a Pretty.space (Pretty.string "{") 1596 | 1597 | close = 1598 | Pretty.a (Pretty.string "}") Pretty.line 1599 | in 1600 | case fields of 1601 | [] -> 1602 | Pretty.string "{}" 1603 | 1604 | _ -> 1605 | fields 1606 | |> List.map (Tuple.mapBoth denode denode) 1607 | |> List.map prettyFieldTypeAnn 1608 | |> Pretty.separators ", " 1609 | |> Pretty.surround open close 1610 | |> Pretty.group 1611 | 1612 | 1613 | prettyGenericRecord : String -> List RecordField -> Doc Tag 1614 | prettyGenericRecord paramName fields = 1615 | let 1616 | open = 1617 | [ Pretty.string "{" 1618 | , statement paramName 1619 | ] 1620 | |> Pretty.words 1621 | |> Pretty.a Pretty.line 1622 | 1623 | close = 1624 | Pretty.a (Pretty.string "}") 1625 | Pretty.line 1626 | 1627 | addBarToFirst exprs = 1628 | case exprs of 1629 | [] -> 1630 | [] 1631 | 1632 | hd :: tl -> 1633 | Pretty.a hd (Pretty.string "| ") :: tl 1634 | in 1635 | case fields of 1636 | [] -> 1637 | Pretty.string "{}" 1638 | 1639 | _ -> 1640 | open 1641 | |> Pretty.a 1642 | (fields 1643 | |> List.map (Tuple.mapBoth denode denode) 1644 | |> List.map prettyFieldTypeAnn 1645 | |> addBarToFirst 1646 | |> Pretty.separators ", " 1647 | ) 1648 | |> Pretty.nest 4 1649 | |> Pretty.surround Pretty.empty close 1650 | |> Pretty.group 1651 | 1652 | 1653 | prettyFieldTypeAnn : ( String, TypeAnnotation ) -> Doc Tag 1654 | prettyFieldTypeAnn ( name, ann ) = 1655 | [ [ statement name 1656 | , Pretty.string ":" 1657 | ] 1658 | |> Pretty.words 1659 | , prettyTypeAnnotation ann 1660 | ] 1661 | |> Pretty.lines 1662 | |> Pretty.nest 4 1663 | |> Pretty.group 1664 | 1665 | 1666 | prettyFunctionTypeAnnotation : Node TypeAnnotation -> Node TypeAnnotation -> Doc Tag 1667 | prettyFunctionTypeAnnotation left right = 1668 | let 1669 | expandLeft : TypeAnnotation -> Doc Tag 1670 | expandLeft ann = 1671 | case ann of 1672 | FunctionTypeAnnotation _ _ -> 1673 | prettyTypeAnnotationParens ann 1674 | 1675 | _ -> 1676 | prettyTypeAnnotation ann 1677 | 1678 | expandRight : TypeAnnotation -> List (Doc Tag) 1679 | expandRight ann = 1680 | case ann of 1681 | FunctionTypeAnnotation innerLeft innerRight -> 1682 | innerFnTypeAnn innerLeft innerRight 1683 | 1684 | _ -> 1685 | [ prettyTypeAnnotation ann ] 1686 | 1687 | innerFnTypeAnn : Node TypeAnnotation -> Node TypeAnnotation -> List (Doc Tag) 1688 | innerFnTypeAnn innerLeft innerRight = 1689 | let 1690 | rightSide = 1691 | denode innerRight |> expandRight 1692 | in 1693 | case rightSide of 1694 | hd :: tl -> 1695 | (denode innerLeft |> expandLeft) 1696 | :: ([ Pretty.string "->", hd ] |> Pretty.words) 1697 | :: tl 1698 | 1699 | [] -> 1700 | [] 1701 | in 1702 | innerFnTypeAnn left right 1703 | |> Pretty.lines 1704 | |> Pretty.group 1705 | 1706 | 1707 | {-| A type annotation is a naked compound if it is made up of multiple parts that 1708 | are not enclosed in brackets or braces. This means either a type or type alias with 1709 | arguments or a function type; records and tuples are compound but enclosed in brackets 1710 | or braces. 1711 | 1712 | Naked type annotations need to be bracketed in situations type argument bindings are 1713 | ambiguous otherwise. 1714 | 1715 | -} 1716 | isNakedCompound : TypeAnnotation -> Bool 1717 | isNakedCompound typeAnn = 1718 | case typeAnn of 1719 | Typed _ [] -> 1720 | False 1721 | 1722 | Typed _ args -> 1723 | True 1724 | 1725 | FunctionTypeAnnotation _ _ -> 1726 | True 1727 | 1728 | _ -> 1729 | False 1730 | 1731 | 1732 | 1733 | --== Helpers 1734 | 1735 | 1736 | prettyMaybe : (a -> Doc Tag) -> Maybe a -> Doc Tag 1737 | prettyMaybe prettyFn maybeVal = 1738 | Maybe.map prettyFn maybeVal 1739 | |> Maybe.withDefault Pretty.empty 1740 | 1741 | 1742 | decrementIndent : Int -> Int -> Int 1743 | decrementIndent currentIndent spaces = 1744 | let 1745 | modded = 1746 | modBy 4 (currentIndent - spaces) 1747 | in 1748 | if modded == 0 then 1749 | 4 1750 | 1751 | else 1752 | modded 1753 | 1754 | 1755 | dot : Doc Tag 1756 | dot = 1757 | Pretty.string "." 1758 | 1759 | 1760 | quotes : Doc Tag -> Doc Tag 1761 | quotes doc = 1762 | Pretty.surround (literal "\"") (literal "\"") doc 1763 | 1764 | 1765 | tripleQuotes : Doc Tag -> Doc Tag 1766 | tripleQuotes doc = 1767 | Pretty.surround (literal "\"\"\"") (literal "\"\"\"") doc 1768 | 1769 | 1770 | singleQuotes : Doc Tag -> Doc Tag 1771 | singleQuotes doc = 1772 | Pretty.surround (literal "'") (literal "'") doc 1773 | 1774 | 1775 | sqParens : Doc Tag -> Doc Tag 1776 | sqParens doc = 1777 | Pretty.surround (Pretty.string "[") (Pretty.string "]") doc 1778 | 1779 | 1780 | doubleLines : List (Doc Tag) -> Doc Tag 1781 | doubleLines = 1782 | Pretty.join (Pretty.a Pretty.line Pretty.line) 1783 | 1784 | 1785 | escape : String -> String 1786 | escape val = 1787 | val 1788 | |> String.replace "\\" "\\\\" 1789 | |> String.replace "\"" "\\\"" 1790 | |> String.replace "\n" "\\n" 1791 | |> String.replace "\t" "\\t" 1792 | |> String.replace "\u{000D}" "\\u{000D}" 1793 | |> hexEscapeNonPrintCharacters 1794 | 1795 | 1796 | escapeChar : Char -> String 1797 | escapeChar character = 1798 | case character of 1799 | '\\' -> 1800 | "\\\\" 1801 | 1802 | '\'' -> 1803 | "\\'" 1804 | 1805 | '\t' -> 1806 | "\\t" 1807 | 1808 | '\n' -> 1809 | "\\n" 1810 | 1811 | '\u{000D}' -> 1812 | "\\u{000D}" 1813 | 1814 | otherCharacter -> 1815 | if characterIsPrint otherCharacter then 1816 | "\\u{" ++ characterHex otherCharacter ++ "}" 1817 | 1818 | else 1819 | String.fromChar otherCharacter 1820 | 1821 | 1822 | hexEscapeNonPrintCharacters : String -> String 1823 | hexEscapeNonPrintCharacters val = 1824 | val 1825 | |> String.toList 1826 | |> List.map 1827 | (\character -> 1828 | if characterIsPrint character then 1829 | "\\u{" ++ characterHex character ++ "}" 1830 | 1831 | else 1832 | String.fromChar character 1833 | ) 1834 | |> String.concat 1835 | 1836 | 1837 | characterHex : Char -> String 1838 | characterHex character = 1839 | String.toUpper (Hex.toString (Char.toCode character)) 1840 | 1841 | 1842 | characterIsPrint : Char -> Bool 1843 | characterIsPrint character = 1844 | case Unicode.getCategory character of 1845 | Nothing -> 1846 | False 1847 | 1848 | Just category -> 1849 | case category of 1850 | Unicode.SeparatorLine -> 1851 | True 1852 | 1853 | Unicode.SeparatorParagraph -> 1854 | True 1855 | 1856 | Unicode.OtherControl -> 1857 | True 1858 | 1859 | Unicode.OtherFormat -> 1860 | True 1861 | 1862 | Unicode.OtherSurrogate -> 1863 | True 1864 | 1865 | Unicode.OtherPrivateUse -> 1866 | True 1867 | 1868 | Unicode.OtherNotAssigned -> 1869 | True 1870 | 1871 | _ -> 1872 | False 1873 | 1874 | 1875 | optionalGroup : Bool -> Doc Tag -> Doc Tag 1876 | optionalGroup flag doc = 1877 | if flag then 1878 | doc 1879 | 1880 | else 1881 | Pretty.group doc 1882 | 1883 | 1884 | optionalParens : Bool -> Doc Tag -> Doc Tag 1885 | optionalParens flag doc = 1886 | if flag then 1887 | Pretty.parens doc 1888 | 1889 | else 1890 | doc 1891 | 1892 | 1893 | toHexString : Int -> String 1894 | toHexString val = 1895 | let 1896 | padWithZeros str = 1897 | let 1898 | length = 1899 | String.length str 1900 | in 1901 | if length < 2 then 1902 | String.padLeft 2 '0' str 1903 | 1904 | else if length > 2 && length < 4 then 1905 | String.padLeft 4 '0' str 1906 | 1907 | else if length > 4 && length < 8 then 1908 | String.padLeft 8 '0' str 1909 | 1910 | else 1911 | str 1912 | in 1913 | "0x" ++ (Hex.toString val |> String.toUpper |> padWithZeros) 1914 | 1915 | 1916 | {-| Calculate a precedence for any operator to be able to know when 1917 | parenthesis are needed or not. 1918 | 1919 | When a lower precedence expression appears beneath a higher one, its needs 1920 | parenthesis. 1921 | 1922 | When a higher precedence expression appears beneath a lower one, if should 1923 | not have parenthesis. 1924 | 1925 | -} 1926 | precedence : String -> Int 1927 | precedence symbol = 1928 | case symbol of 1929 | ">>" -> 1930 | 9 1931 | 1932 | "<<" -> 1933 | 9 1934 | 1935 | "^" -> 1936 | 8 1937 | 1938 | "*" -> 1939 | 7 1940 | 1941 | "/" -> 1942 | 7 1943 | 1944 | "//" -> 1945 | 7 1946 | 1947 | "%" -> 1948 | 7 1949 | 1950 | "rem" -> 1951 | 7 1952 | 1953 | "+" -> 1954 | 6 1955 | 1956 | "-" -> 1957 | 6 1958 | 1959 | "++" -> 1960 | 5 1961 | 1962 | "::" -> 1963 | 5 1964 | 1965 | "==" -> 1966 | 4 1967 | 1968 | "/=" -> 1969 | 4 1970 | 1971 | "<" -> 1972 | 4 1973 | 1974 | ">" -> 1975 | 4 1976 | 1977 | "<=" -> 1978 | 4 1979 | 1980 | ">=" -> 1981 | 4 1982 | 1983 | "&&" -> 1984 | 3 1985 | 1986 | "||" -> 1987 | 2 1988 | 1989 | "|>" -> 1990 | 0 1991 | 1992 | "<|" -> 1993 | 0 1994 | 1995 | _ -> 1996 | 0 1997 | 1998 | 1999 | {-| Gets the parts of a comment in the correct order. 2000 | -} 2001 | getParts : Comments.Comment a -> List Comments.CommentPart 2002 | getParts (Comments.Comment parts) = 2003 | List.reverse parts 2004 | 2005 | 2006 | {-| Pretty prints a document comment. 2007 | 2008 | Where possible the comment will be re-flowed to fit the specified page width. 2009 | 2010 | -} 2011 | layoutDocComment : Int -> Comments.Comment Comments.DocComment -> String 2012 | layoutDocComment width docComment = 2013 | List.map prettyCommentPart (getParts docComment) 2014 | |> Pretty.lines 2015 | |> delimiters 2016 | |> Pretty.pretty width 2017 | 2018 | 2019 | {-| Pretty prints a file comment. 2020 | 2021 | Where possible the comment will be re-flowed to fit the specified page width. 2022 | 2023 | -} 2024 | layoutFileComment : Int -> Comments.Comment Comments.FileComment -> ( String, List (List String) ) 2025 | layoutFileComment width fileComment = 2026 | let 2027 | ( parts, splits ) = 2028 | layoutTags width (getParts fileComment) 2029 | in 2030 | ( List.map prettyCommentPart parts 2031 | |> Pretty.lines 2032 | |> delimiters 2033 | |> Pretty.pretty width 2034 | , splits 2035 | ) 2036 | 2037 | 2038 | {-| Combines lists of doc tags that are together in the comment into single lists, 2039 | then breaks those lists up to fit the page width. 2040 | -} 2041 | layoutTags : Int -> List Comments.CommentPart -> ( List Comments.CommentPart, List (List String) ) 2042 | layoutTags width parts = 2043 | List.foldr 2044 | (\part ( accumParts, accumDocTags ) -> 2045 | case part of 2046 | Comments.DocTags tags -> 2047 | let 2048 | splits = 2049 | fitAndSplit width tags 2050 | in 2051 | ( List.map Comments.DocTags splits ++ accumParts 2052 | , accumDocTags ++ splits 2053 | ) 2054 | 2055 | otherPart -> 2056 | ( otherPart :: accumParts, accumDocTags ) 2057 | ) 2058 | ( [], [] ) 2059 | (mergeDocTags parts) 2060 | 2061 | 2062 | {-| Takes tags from the input and builds them into an output list until the 2063 | given width limit cannot be kept to. When the width limit is breached the output 2064 | spills over into more lists. 2065 | 2066 | Each list must contain at least one tag, even if this were to breach the width 2067 | limit. 2068 | 2069 | -} 2070 | fitAndSplit : Int -> List String -> List (List String) 2071 | fitAndSplit width tags = 2072 | case tags of 2073 | [] -> 2074 | [] 2075 | 2076 | t :: ts -> 2077 | let 2078 | ( splitsExceptLast, lastSplit, _ ) = 2079 | List.foldl 2080 | (\tag ( allSplits, curSplit, remaining ) -> 2081 | if String.length tag <= remaining then 2082 | ( allSplits, tag :: curSplit, remaining - String.length tag ) 2083 | 2084 | else 2085 | ( allSplits ++ [ List.reverse curSplit ], [ tag ], width - String.length tag ) 2086 | ) 2087 | ( [], [ t ], width - String.length t ) 2088 | ts 2089 | in 2090 | splitsExceptLast ++ [ List.reverse lastSplit ] 2091 | 2092 | 2093 | {-| Merges neighbouring lists of doc tags together. 2094 | -} 2095 | mergeDocTags : List Comments.CommentPart -> List Comments.CommentPart 2096 | mergeDocTags innerParts = 2097 | let 2098 | ( partsExceptMaybeFirst, maybeFirstPart ) = 2099 | List.foldr 2100 | (\part ( accum, context ) -> 2101 | case context of 2102 | Nothing -> 2103 | case part of 2104 | Comments.DocTags tags -> 2105 | ( accum, Just tags ) 2106 | 2107 | otherPart -> 2108 | ( otherPart :: accum, Nothing ) 2109 | 2110 | Just contextTags -> 2111 | case part of 2112 | Comments.DocTags tags -> 2113 | ( accum, Just (contextTags ++ tags) ) 2114 | 2115 | otherPart -> 2116 | ( otherPart :: Comments.DocTags (List.sort contextTags) :: accum, Nothing ) 2117 | ) 2118 | ( [], Nothing ) 2119 | innerParts 2120 | in 2121 | case maybeFirstPart of 2122 | Nothing -> 2123 | partsExceptMaybeFirst 2124 | 2125 | Just tags -> 2126 | Comments.DocTags (List.sort tags) :: partsExceptMaybeFirst 2127 | 2128 | 2129 | prettyCommentPart : Comments.CommentPart -> Doc Tag 2130 | prettyCommentPart part = 2131 | case part of 2132 | Comments.Markdown val -> 2133 | prettyMarkdown val 2134 | 2135 | Comments.Code val -> 2136 | prettyCode val 2137 | 2138 | Comments.DocTags tags -> 2139 | prettyTags tags 2140 | 2141 | 2142 | prettyMarkdown val = 2143 | Pretty.string val 2144 | |> Pretty.a Pretty.line 2145 | 2146 | 2147 | prettyCode val = 2148 | Pretty.string val 2149 | |> Pretty.indent 4 2150 | 2151 | 2152 | prettyTags tags = 2153 | [ Pretty.string "@docs" 2154 | , List.map Pretty.string tags 2155 | |> Pretty.join (Pretty.string ", ") 2156 | ] 2157 | |> Pretty.words 2158 | |> Pretty.a Pretty.line 2159 | 2160 | 2161 | partToStringAndTags : Int -> Comments.CommentPart -> ( String, List String ) 2162 | partToStringAndTags width part = 2163 | case part of 2164 | Comments.Markdown val -> 2165 | ( val, [] ) 2166 | 2167 | Comments.Code val -> 2168 | ( " " ++ val, [] ) 2169 | 2170 | Comments.DocTags tags -> 2171 | ( "@doc " ++ String.join ", " tags, tags ) 2172 | 2173 | 2174 | docCommentParser : Parser (Comments.Comment Comments.DocComment) 2175 | docCommentParser = 2176 | Parser.getSource 2177 | |> Parser.map (\val -> Comments.Comment [ Comments.Markdown val ]) 2178 | 2179 | 2180 | fileCommentParser : Parser (Comments.Comment Comments.FileComment) 2181 | fileCommentParser = 2182 | Parser.getSource 2183 | |> Parser.map (\val -> Comments.Comment [ Comments.Markdown val ]) 2184 | 2185 | 2186 | delimiters : Doc Tag -> Doc Tag 2187 | delimiters doc = 2188 | Pretty.string "{-| " 2189 | |> Pretty.a doc 2190 | |> Pretty.a Pretty.line 2191 | |> Pretty.a (Pretty.string "-}") 2192 | -------------------------------------------------------------------------------- /src/ImportsAndExposing.elm: -------------------------------------------------------------------------------- 1 | module ImportsAndExposing exposing (sortAndDedupExposings, sortAndDedupImports) 2 | 3 | import Elm.Syntax.Exposing exposing (Exposing(..), TopLevelExpose(..)) 4 | import Elm.Syntax.Import exposing (Import) 5 | import Elm.Syntax.Node as Node exposing (Node(..)) 6 | import Elm.Syntax.Range exposing (emptyRange) 7 | import Maybe.Extra 8 | 9 | 10 | 11 | -- Sorting and deduplicating exposings. 12 | 13 | 14 | sortAndDedupExposings : List TopLevelExpose -> List TopLevelExpose 15 | sortAndDedupExposings tlExposings = 16 | List.sortWith topLevelExposeOrder tlExposings 17 | |> groupByExposingName 18 | |> List.map combineTopLevelExposes 19 | 20 | 21 | topLevelExposeOrder : TopLevelExpose -> TopLevelExpose -> Order 22 | topLevelExposeOrder tlel tler = 23 | case ( tlel, tler ) of 24 | ( InfixExpose _, InfixExpose _ ) -> 25 | compare (topLevelExposeName tlel) (topLevelExposeName tler) 26 | 27 | ( InfixExpose _, _ ) -> 28 | LT 29 | 30 | ( _, InfixExpose _ ) -> 31 | GT 32 | 33 | ( _, _ ) -> 34 | compare (topLevelExposeName tlel) (topLevelExposeName tler) 35 | 36 | 37 | topLevelExposeName : TopLevelExpose -> String 38 | topLevelExposeName tle = 39 | case tle of 40 | InfixExpose val -> 41 | val 42 | 43 | FunctionExpose val -> 44 | val 45 | 46 | TypeOrAliasExpose val -> 47 | val 48 | 49 | TypeExpose exposedType -> 50 | exposedType.name 51 | 52 | 53 | groupByExposingName : List TopLevelExpose -> List (List TopLevelExpose) 54 | groupByExposingName innerImports = 55 | let 56 | ( _, hdGroup, remGroups ) = 57 | case innerImports of 58 | [] -> 59 | ( "", [], [ [] ] ) 60 | 61 | hd :: _ -> 62 | List.foldl 63 | (\exp ( currName, currAccum, accum ) -> 64 | let 65 | nextName = 66 | topLevelExposeName exp 67 | in 68 | if nextName == currName then 69 | ( currName, exp :: currAccum, accum ) 70 | 71 | else 72 | ( nextName, [ exp ], currAccum :: accum ) 73 | ) 74 | ( topLevelExposeName hd, [], [] ) 75 | innerImports 76 | in 77 | (hdGroup :: remGroups) |> List.reverse 78 | 79 | 80 | combineTopLevelExposes : List TopLevelExpose -> TopLevelExpose 81 | combineTopLevelExposes exposes = 82 | case exposes of 83 | [] -> 84 | InfixExpose "" 85 | 86 | hd :: tl -> 87 | List.foldl 88 | (\exp result -> 89 | case ( exp, result ) of 90 | ( TypeExpose typeExpose, _ ) -> 91 | case typeExpose.open of 92 | Just _ -> 93 | exp 94 | 95 | _ -> 96 | result 97 | 98 | ( _, TypeExpose typeExpose ) -> 99 | case typeExpose.open of 100 | Just _ -> 101 | result 102 | 103 | _ -> 104 | exp 105 | 106 | ( _, _ ) -> 107 | result 108 | ) 109 | hd 110 | tl 111 | 112 | 113 | joinMaybeExposings : Maybe Exposing -> Maybe Exposing -> Maybe Exposing 114 | joinMaybeExposings maybeLeft maybeRight = 115 | case ( maybeLeft, maybeRight ) of 116 | ( Nothing, Nothing ) -> 117 | Nothing 118 | 119 | ( Just left, Nothing ) -> 120 | Just left 121 | 122 | ( Nothing, Just right ) -> 123 | Just right 124 | 125 | ( Just left, Just right ) -> 126 | joinExposings left right |> Just 127 | 128 | 129 | joinExposings : Exposing -> Exposing -> Exposing 130 | joinExposings left right = 131 | case ( left, right ) of 132 | ( All range, _ ) -> 133 | All range 134 | 135 | ( _, All range ) -> 136 | All range 137 | 138 | ( Explicit leftNodes, Explicit rightNodes ) -> 139 | List.append (denodeAll leftNodes) (denodeAll rightNodes) 140 | --|> sortAndDedupExposings 141 | |> nodifyAll 142 | |> Explicit 143 | 144 | 145 | sortAndDedupExposing : Exposing -> Exposing 146 | sortAndDedupExposing exp = 147 | case exp of 148 | All range -> 149 | All range 150 | 151 | Explicit nodes -> 152 | denodeAll nodes 153 | |> sortAndDedupExposings 154 | |> nodifyAll 155 | |> Explicit 156 | 157 | 158 | 159 | -- Sorting and deduplicating imports. 160 | 161 | 162 | sortAndDedupImports : List Import -> List Import 163 | sortAndDedupImports imports = 164 | let 165 | impName imp = 166 | denode imp.moduleName 167 | in 168 | List.sortBy impName imports 169 | |> groupByModuleName 170 | |> List.map combineImports 171 | 172 | 173 | groupByModuleName : List Import -> List (List Import) 174 | groupByModuleName innerImports = 175 | let 176 | ( _, hdGroup, remGroups ) = 177 | case innerImports of 178 | [] -> 179 | ( [], [], [ [] ] ) 180 | 181 | hd :: _ -> 182 | List.foldl 183 | (\imp ( currName, currAccum, accum ) -> 184 | let 185 | nextName = 186 | denode imp.moduleName 187 | in 188 | if nextName == currName then 189 | ( currName, imp :: currAccum, accum ) 190 | 191 | else 192 | ( nextName, [ imp ], currAccum :: accum ) 193 | ) 194 | ( denode hd.moduleName, [], [] ) 195 | innerImports 196 | in 197 | (hdGroup :: remGroups) |> List.reverse 198 | 199 | 200 | combineImports : List Import -> Import 201 | combineImports innerImports = 202 | case innerImports of 203 | [] -> 204 | { moduleName = nodify [] 205 | , moduleAlias = Nothing 206 | , exposingList = Nothing 207 | } 208 | 209 | hd :: tl -> 210 | let 211 | combinedImports = 212 | List.foldl 213 | (\imp result -> 214 | { moduleName = imp.moduleName 215 | , moduleAlias = Maybe.Extra.or imp.moduleAlias result.moduleAlias 216 | , exposingList = 217 | joinMaybeExposings (denodeMaybe imp.exposingList) (denodeMaybe result.exposingList) 218 | |> nodifyMaybe 219 | } 220 | ) 221 | hd 222 | tl 223 | in 224 | { combinedImports 225 | | exposingList = 226 | Maybe.map (denode >> sortAndDedupExposing >> nodify) 227 | combinedImports.exposingList 228 | } 229 | 230 | 231 | 232 | -- Helper functions 233 | 234 | 235 | denode : Node a -> a 236 | denode = 237 | Node.value 238 | 239 | 240 | denodeAll : List (Node a) -> List a 241 | denodeAll = 242 | List.map denode 243 | 244 | 245 | denodeMaybe : Maybe (Node a) -> Maybe a 246 | denodeMaybe = 247 | Maybe.map denode 248 | 249 | 250 | nodify : a -> Node a 251 | nodify exp = 252 | Node emptyRange exp 253 | 254 | 255 | nodifyAll : List a -> List (Node a) 256 | nodifyAll = 257 | List.map nodify 258 | 259 | 260 | nodifyMaybe : Maybe a -> Maybe (Node a) 261 | nodifyMaybe = 262 | Maybe.map nodify 263 | -------------------------------------------------------------------------------- /src/Util.elm: -------------------------------------------------------------------------------- 1 | module Util exposing (denode, denodeAll, denodeMaybe, nodify, nodifyAll, nodifyAndParentifyAll, nodifyMaybe, parentify) 2 | 3 | import Elm.Syntax.Expression exposing (Expression(..)) 4 | import Elm.Syntax.Infix exposing (InfixDirection(..)) 5 | import Elm.Syntax.Node as Node exposing (Node(..)) 6 | import Elm.Syntax.Range exposing (emptyRange) 7 | 8 | 9 | denode : Node a -> a 10 | denode = 11 | Node.value 12 | 13 | 14 | denodeAll : List (Node a) -> List a 15 | denodeAll = 16 | List.map denode 17 | 18 | 19 | denodeMaybe : Maybe (Node a) -> Maybe a 20 | denodeMaybe = 21 | Maybe.map denode 22 | 23 | 24 | nodify : a -> Node a 25 | nodify exp = 26 | Node emptyRange exp 27 | 28 | 29 | nodifyAll : List a -> List (Node a) 30 | nodifyAll = 31 | List.map nodify 32 | 33 | 34 | nodifyMaybe : Maybe a -> Maybe (Node a) 35 | nodifyMaybe = 36 | Maybe.map nodify 37 | 38 | 39 | nodifyAndParentifyAll : Int -> InfixDirection -> List Expression -> List (Node Expression) 40 | nodifyAndParentifyAll precedence associativity = 41 | List.map (parentify precedence associativity >> nodify) 42 | 43 | 44 | parentify : Int -> InfixDirection -> Expression -> Expression 45 | parentify precedence associativity expr = 46 | case expr of 47 | Application apps -> 48 | if List.length apps == 1 then 49 | expr 50 | 51 | else if precedence == 10 then 52 | ParenthesizedExpression (nodify expr) 53 | 54 | else 55 | expr 56 | 57 | OperatorApplication symbol opAssociativity _ _ -> 58 | if precedence > symbolToPrecedence symbol || (precedence == symbolToPrecedence symbol && associativity /= opAssociativity) then 59 | ParenthesizedExpression (nodify expr) 60 | 61 | else 62 | expr 63 | 64 | IfBlock _ _ _ -> 65 | ParenthesizedExpression (nodify expr) 66 | 67 | LetExpression _ -> 68 | ParenthesizedExpression (nodify expr) 69 | 70 | CaseExpression _ -> 71 | ParenthesizedExpression (nodify expr) 72 | 73 | _ -> 74 | expr 75 | 76 | 77 | symbolToPrecedence : String -> Int 78 | symbolToPrecedence symbol = 79 | case symbol of 80 | ">>" -> 81 | 9 82 | 83 | "<<" -> 84 | 9 85 | 86 | "^" -> 87 | 8 88 | 89 | "*" -> 90 | 7 91 | 92 | "/" -> 93 | 7 94 | 95 | "//" -> 96 | 7 97 | 98 | "%" -> 99 | 7 100 | 101 | "rem" -> 102 | 7 103 | 104 | "+" -> 105 | 6 106 | 107 | "-" -> 108 | 6 109 | 110 | "++" -> 111 | 5 112 | 113 | "::" -> 114 | 5 115 | 116 | "==" -> 117 | 4 118 | 119 | "/=" -> 120 | 4 121 | 122 | "<" -> 123 | 4 124 | 125 | ">" -> 126 | 4 127 | 128 | "<=" -> 129 | 4 130 | 131 | ">=" -> 132 | 4 133 | 134 | "&&" -> 135 | 3 136 | 137 | "||" -> 138 | 2 139 | 140 | "|>" -> 141 | 0 142 | 143 | "<|" -> 144 | 0 145 | 146 | _ -> 147 | -1 148 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | example.txt 2 | pre/ 3 | post/ 4 | examples/ 5 | -------------------------------------------------------------------------------- /test/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src/elm", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "Chadtech/elm-bool-extra": "2.4.0", 11 | "elm/browser": "1.0.1", 12 | "elm/core": "1.0.2", 13 | "elm/html": "1.0.0", 14 | "elm/parser": "1.1.0", 15 | "elm-community/basics-extra": "4.0.0", 16 | "elm-community/maybe-extra": "5.0.0", 17 | "rtfeldman/elm-hex": "1.0.0", 18 | "stil4m/elm-syntax": "7.1.0", 19 | "the-sett/elm-pretty-printer": "3.0.0" 20 | }, 21 | "indirect": { 22 | "elm/json": "1.1.3", 23 | "elm/time": "1.0.0", 24 | "elm/url": "1.0.0", 25 | "elm/virtual-dom": "1.0.2", 26 | "elm-community/json-extra": "4.0.0", 27 | "elm-community/list-extra": "8.2.2", 28 | "rtfeldman/elm-iso8601-date-strings": "1.1.3", 29 | "stil4m/structured-writer": "1.0.2" 30 | } 31 | }, 32 | "test-dependencies": { 33 | "direct": {}, 34 | "indirect": {} 35 | } 36 | } -------------------------------------------------------------------------------- /test/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tink-data", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tink-data", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "glob": "^7.1.4" 13 | }, 14 | "devDependencies": { 15 | "elm": "^0.19.1-5" 16 | }, 17 | "engines": { 18 | "node": ">=6.10.0" 19 | } 20 | }, 21 | "node_modules/ajv": { 22 | "version": "6.12.6", 23 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 24 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 25 | "dev": true, 26 | "dependencies": { 27 | "fast-deep-equal": "^3.1.1", 28 | "fast-json-stable-stringify": "^2.0.0", 29 | "json-schema-traverse": "^0.4.1", 30 | "uri-js": "^4.2.2" 31 | }, 32 | "funding": { 33 | "type": "github", 34 | "url": "https://github.com/sponsors/epoberezkin" 35 | } 36 | }, 37 | "node_modules/asn1": { 38 | "version": "0.2.6", 39 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", 40 | "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", 41 | "dev": true, 42 | "dependencies": { 43 | "safer-buffer": "~2.1.0" 44 | } 45 | }, 46 | "node_modules/assert-plus": { 47 | "version": "1.0.0", 48 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 49 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", 50 | "dev": true, 51 | "engines": { 52 | "node": ">=0.8" 53 | } 54 | }, 55 | "node_modules/asynckit": { 56 | "version": "0.4.0", 57 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 58 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 59 | "dev": true 60 | }, 61 | "node_modules/aws-sign2": { 62 | "version": "0.7.0", 63 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 64 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", 65 | "dev": true, 66 | "engines": { 67 | "node": "*" 68 | } 69 | }, 70 | "node_modules/aws4": { 71 | "version": "1.11.0", 72 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", 73 | "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", 74 | "dev": true 75 | }, 76 | "node_modules/balanced-match": { 77 | "version": "1.0.0", 78 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 79 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 80 | }, 81 | "node_modules/bcrypt-pbkdf": { 82 | "version": "1.0.2", 83 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 84 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 85 | "dev": true, 86 | "dependencies": { 87 | "tweetnacl": "^0.14.3" 88 | } 89 | }, 90 | "node_modules/brace-expansion": { 91 | "version": "1.1.11", 92 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 93 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 94 | "dependencies": { 95 | "balanced-match": "^1.0.0", 96 | "concat-map": "0.0.1" 97 | } 98 | }, 99 | "node_modules/caseless": { 100 | "version": "0.12.0", 101 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 102 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", 103 | "dev": true 104 | }, 105 | "node_modules/combined-stream": { 106 | "version": "1.0.8", 107 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 108 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 109 | "dev": true, 110 | "dependencies": { 111 | "delayed-stream": "~1.0.0" 112 | }, 113 | "engines": { 114 | "node": ">= 0.8" 115 | } 116 | }, 117 | "node_modules/concat-map": { 118 | "version": "0.0.1", 119 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 120 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 121 | }, 122 | "node_modules/core-util-is": { 123 | "version": "1.0.2", 124 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 125 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 126 | "dev": true 127 | }, 128 | "node_modules/dashdash": { 129 | "version": "1.14.1", 130 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 131 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 132 | "dev": true, 133 | "dependencies": { 134 | "assert-plus": "^1.0.0" 135 | }, 136 | "engines": { 137 | "node": ">=0.10" 138 | } 139 | }, 140 | "node_modules/delayed-stream": { 141 | "version": "1.0.0", 142 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 143 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 144 | "dev": true, 145 | "engines": { 146 | "node": ">=0.4.0" 147 | } 148 | }, 149 | "node_modules/ecc-jsbn": { 150 | "version": "0.1.2", 151 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 152 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 153 | "dev": true, 154 | "dependencies": { 155 | "jsbn": "~0.1.0", 156 | "safer-buffer": "^2.1.0" 157 | } 158 | }, 159 | "node_modules/elm": { 160 | "version": "0.19.1-5", 161 | "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.1-5.tgz", 162 | "integrity": "sha512-dyBoPvFiNLvxOStQJdyq28gZEjS/enZXdZ5yyCtNtDEMbFJJVQq4pYNRKvhrKKdlxNot6d96iQe1uczoqO5yvA==", 163 | "dev": true, 164 | "hasInstallScript": true, 165 | "dependencies": { 166 | "request": "^2.88.0" 167 | }, 168 | "bin": { 169 | "elm": "bin/elm" 170 | }, 171 | "engines": { 172 | "node": ">=7.0.0" 173 | } 174 | }, 175 | "node_modules/extend": { 176 | "version": "3.0.2", 177 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 178 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 179 | "dev": true 180 | }, 181 | "node_modules/extsprintf": { 182 | "version": "1.3.0", 183 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 184 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", 185 | "dev": true, 186 | "engines": [ 187 | "node >=0.6.0" 188 | ] 189 | }, 190 | "node_modules/fast-deep-equal": { 191 | "version": "3.1.3", 192 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 193 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 194 | "dev": true 195 | }, 196 | "node_modules/fast-json-stable-stringify": { 197 | "version": "2.1.0", 198 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 199 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 200 | "dev": true 201 | }, 202 | "node_modules/forever-agent": { 203 | "version": "0.6.1", 204 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 205 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", 206 | "dev": true, 207 | "engines": { 208 | "node": "*" 209 | } 210 | }, 211 | "node_modules/form-data": { 212 | "version": "2.3.3", 213 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 214 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 215 | "dev": true, 216 | "dependencies": { 217 | "asynckit": "^0.4.0", 218 | "combined-stream": "^1.0.6", 219 | "mime-types": "^2.1.12" 220 | }, 221 | "engines": { 222 | "node": ">= 0.12" 223 | } 224 | }, 225 | "node_modules/fs.realpath": { 226 | "version": "1.0.0", 227 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 228 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 229 | }, 230 | "node_modules/getpass": { 231 | "version": "0.1.7", 232 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 233 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 234 | "dev": true, 235 | "dependencies": { 236 | "assert-plus": "^1.0.0" 237 | } 238 | }, 239 | "node_modules/glob": { 240 | "version": "7.1.4", 241 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 242 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 243 | "dependencies": { 244 | "fs.realpath": "^1.0.0", 245 | "inflight": "^1.0.4", 246 | "inherits": "2", 247 | "minimatch": "^3.0.4", 248 | "once": "^1.3.0", 249 | "path-is-absolute": "^1.0.0" 250 | }, 251 | "engines": { 252 | "node": "*" 253 | } 254 | }, 255 | "node_modules/har-schema": { 256 | "version": "2.0.0", 257 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 258 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", 259 | "dev": true, 260 | "engines": { 261 | "node": ">=4" 262 | } 263 | }, 264 | "node_modules/har-validator": { 265 | "version": "5.1.5", 266 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", 267 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", 268 | "deprecated": "this library is no longer supported", 269 | "dev": true, 270 | "dependencies": { 271 | "ajv": "^6.12.3", 272 | "har-schema": "^2.0.0" 273 | }, 274 | "engines": { 275 | "node": ">=6" 276 | } 277 | }, 278 | "node_modules/http-signature": { 279 | "version": "1.2.0", 280 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 281 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 282 | "dev": true, 283 | "dependencies": { 284 | "assert-plus": "^1.0.0", 285 | "jsprim": "^1.2.2", 286 | "sshpk": "^1.7.0" 287 | }, 288 | "engines": { 289 | "node": ">=0.8", 290 | "npm": ">=1.3.7" 291 | } 292 | }, 293 | "node_modules/inflight": { 294 | "version": "1.0.6", 295 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 296 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 297 | "dependencies": { 298 | "once": "^1.3.0", 299 | "wrappy": "1" 300 | } 301 | }, 302 | "node_modules/inherits": { 303 | "version": "2.0.4", 304 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 305 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 306 | }, 307 | "node_modules/is-typedarray": { 308 | "version": "1.0.0", 309 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 310 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", 311 | "dev": true 312 | }, 313 | "node_modules/isstream": { 314 | "version": "0.1.2", 315 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 316 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", 317 | "dev": true 318 | }, 319 | "node_modules/jsbn": { 320 | "version": "0.1.1", 321 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 322 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 323 | "dev": true 324 | }, 325 | "node_modules/json-schema": { 326 | "version": "0.4.0", 327 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", 328 | "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", 329 | "dev": true 330 | }, 331 | "node_modules/json-schema-traverse": { 332 | "version": "0.4.1", 333 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 334 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 335 | "dev": true 336 | }, 337 | "node_modules/json-stringify-safe": { 338 | "version": "5.0.1", 339 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 340 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", 341 | "dev": true 342 | }, 343 | "node_modules/jsprim": { 344 | "version": "1.4.2", 345 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", 346 | "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", 347 | "dev": true, 348 | "dependencies": { 349 | "assert-plus": "1.0.0", 350 | "extsprintf": "1.3.0", 351 | "json-schema": "0.4.0", 352 | "verror": "1.10.0" 353 | }, 354 | "engines": { 355 | "node": ">=0.6.0" 356 | } 357 | }, 358 | "node_modules/mime-db": { 359 | "version": "1.51.0", 360 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 361 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", 362 | "dev": true, 363 | "engines": { 364 | "node": ">= 0.6" 365 | } 366 | }, 367 | "node_modules/mime-types": { 368 | "version": "2.1.34", 369 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 370 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 371 | "dev": true, 372 | "dependencies": { 373 | "mime-db": "1.51.0" 374 | }, 375 | "engines": { 376 | "node": ">= 0.6" 377 | } 378 | }, 379 | "node_modules/minimatch": { 380 | "version": "3.0.4", 381 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 382 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 383 | "dependencies": { 384 | "brace-expansion": "^1.1.7" 385 | }, 386 | "engines": { 387 | "node": "*" 388 | } 389 | }, 390 | "node_modules/oauth-sign": { 391 | "version": "0.9.0", 392 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 393 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", 394 | "dev": true, 395 | "engines": { 396 | "node": "*" 397 | } 398 | }, 399 | "node_modules/once": { 400 | "version": "1.4.0", 401 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 402 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 403 | "dependencies": { 404 | "wrappy": "1" 405 | } 406 | }, 407 | "node_modules/path-is-absolute": { 408 | "version": "1.0.1", 409 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 410 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 411 | "engines": { 412 | "node": ">=0.10.0" 413 | } 414 | }, 415 | "node_modules/performance-now": { 416 | "version": "2.1.0", 417 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 418 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", 419 | "dev": true 420 | }, 421 | "node_modules/psl": { 422 | "version": "1.8.0", 423 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 424 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", 425 | "dev": true 426 | }, 427 | "node_modules/punycode": { 428 | "version": "2.1.1", 429 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 430 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 431 | "dev": true, 432 | "engines": { 433 | "node": ">=6" 434 | } 435 | }, 436 | "node_modules/qs": { 437 | "version": "6.5.2", 438 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 439 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", 440 | "dev": true, 441 | "engines": { 442 | "node": ">=0.6" 443 | } 444 | }, 445 | "node_modules/request": { 446 | "version": "2.88.2", 447 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", 448 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", 449 | "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", 450 | "dev": true, 451 | "dependencies": { 452 | "aws-sign2": "~0.7.0", 453 | "aws4": "^1.8.0", 454 | "caseless": "~0.12.0", 455 | "combined-stream": "~1.0.6", 456 | "extend": "~3.0.2", 457 | "forever-agent": "~0.6.1", 458 | "form-data": "~2.3.2", 459 | "har-validator": "~5.1.3", 460 | "http-signature": "~1.2.0", 461 | "is-typedarray": "~1.0.0", 462 | "isstream": "~0.1.2", 463 | "json-stringify-safe": "~5.0.1", 464 | "mime-types": "~2.1.19", 465 | "oauth-sign": "~0.9.0", 466 | "performance-now": "^2.1.0", 467 | "qs": "~6.5.2", 468 | "safe-buffer": "^5.1.2", 469 | "tough-cookie": "~2.5.0", 470 | "tunnel-agent": "^0.6.0", 471 | "uuid": "^3.3.2" 472 | }, 473 | "engines": { 474 | "node": ">= 6" 475 | } 476 | }, 477 | "node_modules/safe-buffer": { 478 | "version": "5.2.1", 479 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 480 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 481 | "dev": true, 482 | "funding": [ 483 | { 484 | "type": "github", 485 | "url": "https://github.com/sponsors/feross" 486 | }, 487 | { 488 | "type": "patreon", 489 | "url": "https://www.patreon.com/feross" 490 | }, 491 | { 492 | "type": "consulting", 493 | "url": "https://feross.org/support" 494 | } 495 | ] 496 | }, 497 | "node_modules/safer-buffer": { 498 | "version": "2.1.2", 499 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 500 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 501 | "dev": true 502 | }, 503 | "node_modules/sshpk": { 504 | "version": "1.17.0", 505 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", 506 | "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", 507 | "dev": true, 508 | "dependencies": { 509 | "asn1": "~0.2.3", 510 | "assert-plus": "^1.0.0", 511 | "bcrypt-pbkdf": "^1.0.0", 512 | "dashdash": "^1.12.0", 513 | "ecc-jsbn": "~0.1.1", 514 | "getpass": "^0.1.1", 515 | "jsbn": "~0.1.0", 516 | "safer-buffer": "^2.0.2", 517 | "tweetnacl": "~0.14.0" 518 | }, 519 | "bin": { 520 | "sshpk-conv": "bin/sshpk-conv", 521 | "sshpk-sign": "bin/sshpk-sign", 522 | "sshpk-verify": "bin/sshpk-verify" 523 | }, 524 | "engines": { 525 | "node": ">=0.10.0" 526 | } 527 | }, 528 | "node_modules/tough-cookie": { 529 | "version": "2.5.0", 530 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 531 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 532 | "dev": true, 533 | "dependencies": { 534 | "psl": "^1.1.28", 535 | "punycode": "^2.1.1" 536 | }, 537 | "engines": { 538 | "node": ">=0.8" 539 | } 540 | }, 541 | "node_modules/tunnel-agent": { 542 | "version": "0.6.0", 543 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 544 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 545 | "dev": true, 546 | "dependencies": { 547 | "safe-buffer": "^5.0.1" 548 | }, 549 | "engines": { 550 | "node": "*" 551 | } 552 | }, 553 | "node_modules/tweetnacl": { 554 | "version": "0.14.5", 555 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 556 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 557 | "dev": true 558 | }, 559 | "node_modules/uri-js": { 560 | "version": "4.4.1", 561 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 562 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 563 | "dev": true, 564 | "dependencies": { 565 | "punycode": "^2.1.0" 566 | } 567 | }, 568 | "node_modules/uuid": { 569 | "version": "3.4.0", 570 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 571 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 572 | "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", 573 | "dev": true, 574 | "bin": { 575 | "uuid": "bin/uuid" 576 | } 577 | }, 578 | "node_modules/verror": { 579 | "version": "1.10.0", 580 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 581 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 582 | "dev": true, 583 | "engines": [ 584 | "node >=0.6.0" 585 | ], 586 | "dependencies": { 587 | "assert-plus": "^1.0.0", 588 | "core-util-is": "1.0.2", 589 | "extsprintf": "^1.2.0" 590 | } 591 | }, 592 | "node_modules/wrappy": { 593 | "version": "1.0.2", 594 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 595 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 596 | } 597 | }, 598 | "dependencies": { 599 | "ajv": { 600 | "version": "6.12.6", 601 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 602 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 603 | "dev": true, 604 | "requires": { 605 | "fast-deep-equal": "^3.1.1", 606 | "fast-json-stable-stringify": "^2.0.0", 607 | "json-schema-traverse": "^0.4.1", 608 | "uri-js": "^4.2.2" 609 | } 610 | }, 611 | "asn1": { 612 | "version": "0.2.6", 613 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", 614 | "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", 615 | "dev": true, 616 | "requires": { 617 | "safer-buffer": "~2.1.0" 618 | } 619 | }, 620 | "assert-plus": { 621 | "version": "1.0.0", 622 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 623 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", 624 | "dev": true 625 | }, 626 | "asynckit": { 627 | "version": "0.4.0", 628 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 629 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 630 | "dev": true 631 | }, 632 | "aws-sign2": { 633 | "version": "0.7.0", 634 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 635 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", 636 | "dev": true 637 | }, 638 | "aws4": { 639 | "version": "1.11.0", 640 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", 641 | "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", 642 | "dev": true 643 | }, 644 | "balanced-match": { 645 | "version": "1.0.0", 646 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 647 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 648 | }, 649 | "bcrypt-pbkdf": { 650 | "version": "1.0.2", 651 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 652 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 653 | "dev": true, 654 | "requires": { 655 | "tweetnacl": "^0.14.3" 656 | } 657 | }, 658 | "brace-expansion": { 659 | "version": "1.1.11", 660 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 661 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 662 | "requires": { 663 | "balanced-match": "^1.0.0", 664 | "concat-map": "0.0.1" 665 | } 666 | }, 667 | "caseless": { 668 | "version": "0.12.0", 669 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 670 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", 671 | "dev": true 672 | }, 673 | "combined-stream": { 674 | "version": "1.0.8", 675 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 676 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 677 | "dev": true, 678 | "requires": { 679 | "delayed-stream": "~1.0.0" 680 | } 681 | }, 682 | "concat-map": { 683 | "version": "0.0.1", 684 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 685 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 686 | }, 687 | "core-util-is": { 688 | "version": "1.0.2", 689 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 690 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 691 | "dev": true 692 | }, 693 | "dashdash": { 694 | "version": "1.14.1", 695 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 696 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 697 | "dev": true, 698 | "requires": { 699 | "assert-plus": "^1.0.0" 700 | } 701 | }, 702 | "delayed-stream": { 703 | "version": "1.0.0", 704 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 705 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 706 | "dev": true 707 | }, 708 | "ecc-jsbn": { 709 | "version": "0.1.2", 710 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 711 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 712 | "dev": true, 713 | "requires": { 714 | "jsbn": "~0.1.0", 715 | "safer-buffer": "^2.1.0" 716 | } 717 | }, 718 | "elm": { 719 | "version": "0.19.1-5", 720 | "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.1-5.tgz", 721 | "integrity": "sha512-dyBoPvFiNLvxOStQJdyq28gZEjS/enZXdZ5yyCtNtDEMbFJJVQq4pYNRKvhrKKdlxNot6d96iQe1uczoqO5yvA==", 722 | "dev": true, 723 | "requires": { 724 | "request": "^2.88.0" 725 | } 726 | }, 727 | "extend": { 728 | "version": "3.0.2", 729 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 730 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 731 | "dev": true 732 | }, 733 | "extsprintf": { 734 | "version": "1.3.0", 735 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 736 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", 737 | "dev": true 738 | }, 739 | "fast-deep-equal": { 740 | "version": "3.1.3", 741 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 742 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 743 | "dev": true 744 | }, 745 | "fast-json-stable-stringify": { 746 | "version": "2.1.0", 747 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 748 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 749 | "dev": true 750 | }, 751 | "forever-agent": { 752 | "version": "0.6.1", 753 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 754 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", 755 | "dev": true 756 | }, 757 | "form-data": { 758 | "version": "2.3.3", 759 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 760 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 761 | "dev": true, 762 | "requires": { 763 | "asynckit": "^0.4.0", 764 | "combined-stream": "^1.0.6", 765 | "mime-types": "^2.1.12" 766 | } 767 | }, 768 | "fs.realpath": { 769 | "version": "1.0.0", 770 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 771 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 772 | }, 773 | "getpass": { 774 | "version": "0.1.7", 775 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 776 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 777 | "dev": true, 778 | "requires": { 779 | "assert-plus": "^1.0.0" 780 | } 781 | }, 782 | "glob": { 783 | "version": "7.1.4", 784 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 785 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 786 | "requires": { 787 | "fs.realpath": "^1.0.0", 788 | "inflight": "^1.0.4", 789 | "inherits": "2", 790 | "minimatch": "^3.0.4", 791 | "once": "^1.3.0", 792 | "path-is-absolute": "^1.0.0" 793 | } 794 | }, 795 | "har-schema": { 796 | "version": "2.0.0", 797 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 798 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", 799 | "dev": true 800 | }, 801 | "har-validator": { 802 | "version": "5.1.5", 803 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", 804 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", 805 | "dev": true, 806 | "requires": { 807 | "ajv": "^6.12.3", 808 | "har-schema": "^2.0.0" 809 | } 810 | }, 811 | "http-signature": { 812 | "version": "1.2.0", 813 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 814 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 815 | "dev": true, 816 | "requires": { 817 | "assert-plus": "^1.0.0", 818 | "jsprim": "^1.2.2", 819 | "sshpk": "^1.7.0" 820 | } 821 | }, 822 | "inflight": { 823 | "version": "1.0.6", 824 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 825 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 826 | "requires": { 827 | "once": "^1.3.0", 828 | "wrappy": "1" 829 | } 830 | }, 831 | "inherits": { 832 | "version": "2.0.4", 833 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 834 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 835 | }, 836 | "is-typedarray": { 837 | "version": "1.0.0", 838 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 839 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", 840 | "dev": true 841 | }, 842 | "isstream": { 843 | "version": "0.1.2", 844 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 845 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", 846 | "dev": true 847 | }, 848 | "jsbn": { 849 | "version": "0.1.1", 850 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 851 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 852 | "dev": true 853 | }, 854 | "json-schema": { 855 | "version": "0.4.0", 856 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", 857 | "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", 858 | "dev": true 859 | }, 860 | "json-schema-traverse": { 861 | "version": "0.4.1", 862 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 863 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 864 | "dev": true 865 | }, 866 | "json-stringify-safe": { 867 | "version": "5.0.1", 868 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 869 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", 870 | "dev": true 871 | }, 872 | "jsprim": { 873 | "version": "1.4.2", 874 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", 875 | "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", 876 | "dev": true, 877 | "requires": { 878 | "assert-plus": "1.0.0", 879 | "extsprintf": "1.3.0", 880 | "json-schema": "0.4.0", 881 | "verror": "1.10.0" 882 | } 883 | }, 884 | "mime-db": { 885 | "version": "1.51.0", 886 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 887 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", 888 | "dev": true 889 | }, 890 | "mime-types": { 891 | "version": "2.1.34", 892 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 893 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 894 | "dev": true, 895 | "requires": { 896 | "mime-db": "1.51.0" 897 | } 898 | }, 899 | "minimatch": { 900 | "version": "3.0.4", 901 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 902 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 903 | "requires": { 904 | "brace-expansion": "^1.1.7" 905 | } 906 | }, 907 | "oauth-sign": { 908 | "version": "0.9.0", 909 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 910 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", 911 | "dev": true 912 | }, 913 | "once": { 914 | "version": "1.4.0", 915 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 916 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 917 | "requires": { 918 | "wrappy": "1" 919 | } 920 | }, 921 | "path-is-absolute": { 922 | "version": "1.0.1", 923 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 924 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 925 | }, 926 | "performance-now": { 927 | "version": "2.1.0", 928 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 929 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", 930 | "dev": true 931 | }, 932 | "psl": { 933 | "version": "1.8.0", 934 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 935 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", 936 | "dev": true 937 | }, 938 | "punycode": { 939 | "version": "2.1.1", 940 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 941 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 942 | "dev": true 943 | }, 944 | "qs": { 945 | "version": "6.5.2", 946 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 947 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", 948 | "dev": true 949 | }, 950 | "request": { 951 | "version": "2.88.2", 952 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", 953 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", 954 | "dev": true, 955 | "requires": { 956 | "aws-sign2": "~0.7.0", 957 | "aws4": "^1.8.0", 958 | "caseless": "~0.12.0", 959 | "combined-stream": "~1.0.6", 960 | "extend": "~3.0.2", 961 | "forever-agent": "~0.6.1", 962 | "form-data": "~2.3.2", 963 | "har-validator": "~5.1.3", 964 | "http-signature": "~1.2.0", 965 | "is-typedarray": "~1.0.0", 966 | "isstream": "~0.1.2", 967 | "json-stringify-safe": "~5.0.1", 968 | "mime-types": "~2.1.19", 969 | "oauth-sign": "~0.9.0", 970 | "performance-now": "^2.1.0", 971 | "qs": "~6.5.2", 972 | "safe-buffer": "^5.1.2", 973 | "tough-cookie": "~2.5.0", 974 | "tunnel-agent": "^0.6.0", 975 | "uuid": "^3.3.2" 976 | } 977 | }, 978 | "safe-buffer": { 979 | "version": "5.2.1", 980 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 981 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 982 | "dev": true 983 | }, 984 | "safer-buffer": { 985 | "version": "2.1.2", 986 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 987 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 988 | "dev": true 989 | }, 990 | "sshpk": { 991 | "version": "1.17.0", 992 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", 993 | "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", 994 | "dev": true, 995 | "requires": { 996 | "asn1": "~0.2.3", 997 | "assert-plus": "^1.0.0", 998 | "bcrypt-pbkdf": "^1.0.0", 999 | "dashdash": "^1.12.0", 1000 | "ecc-jsbn": "~0.1.1", 1001 | "getpass": "^0.1.1", 1002 | "jsbn": "~0.1.0", 1003 | "safer-buffer": "^2.0.2", 1004 | "tweetnacl": "~0.14.0" 1005 | } 1006 | }, 1007 | "tough-cookie": { 1008 | "version": "2.5.0", 1009 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 1010 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 1011 | "dev": true, 1012 | "requires": { 1013 | "psl": "^1.1.28", 1014 | "punycode": "^2.1.1" 1015 | } 1016 | }, 1017 | "tunnel-agent": { 1018 | "version": "0.6.0", 1019 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1020 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1021 | "dev": true, 1022 | "requires": { 1023 | "safe-buffer": "^5.0.1" 1024 | } 1025 | }, 1026 | "tweetnacl": { 1027 | "version": "0.14.5", 1028 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1029 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 1030 | "dev": true 1031 | }, 1032 | "uri-js": { 1033 | "version": "4.4.1", 1034 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1035 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1036 | "dev": true, 1037 | "requires": { 1038 | "punycode": "^2.1.0" 1039 | } 1040 | }, 1041 | "uuid": { 1042 | "version": "3.4.0", 1043 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 1044 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 1045 | "dev": true 1046 | }, 1047 | "verror": { 1048 | "version": "1.10.0", 1049 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1050 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1051 | "dev": true, 1052 | "requires": { 1053 | "assert-plus": "^1.0.0", 1054 | "core-util-is": "1.0.2", 1055 | "extsprintf": "^1.2.0" 1056 | } 1057 | }, 1058 | "wrappy": { 1059 | "version": "1.0.2", 1060 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1061 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1062 | } 1063 | } 1064 | } 1065 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tink-data", 3 | "version": "1.0.0", 4 | "engines": { 5 | "node": ">=6.10.0" 6 | }, 7 | "description": "Turn some CSV into POSTs to Tink APIs.", 8 | "author": "Rupert Smith ", 9 | "license": "MIT", 10 | "main": "src-bridge/index.js", 11 | "scripts": { 12 | "clobber": "rm -rf elm-stuff", 13 | "rebuild": "npm run clobber && npm install && npm run build", 14 | "build": "elm make --output=src/elm.js src/elm/Top.elm", 15 | "start": "npm run build && node src/index.js" 16 | }, 17 | "dependencies": { 18 | "glob": "^7.1.4" 19 | }, 20 | "devDependencies": { 21 | "elm": "^0.19.1-5" 22 | } 23 | } -------------------------------------------------------------------------------- /test/src/elm/Editor.elm: -------------------------------------------------------------------------------- 1 | module Editor exposing (main) 2 | 3 | {-| Sample code for Elm.Pretty with Elm.Token. Use `elm repl` to run this code. 4 | -} 5 | 6 | import Html exposing (..) 7 | import Html.Attributes exposing (..) 8 | import Html.Events 9 | import Browser 10 | import Elm.CodeGen exposing (File) 11 | import Elm.Token exposing (Token(..)) 12 | import Elm.DSLParser exposing (parse) 13 | import Elm.Pretty exposing (pretty) 14 | import Pretty.Renderer 15 | import Parser exposing (DeadEnd) 16 | 17 | main = 18 | Browser.sandbox 19 | { init = init 20 | , update = update 21 | , view = view 22 | } 23 | 24 | 25 | type alias Model = 26 | {sourceCode : String } 27 | 28 | type Msg = 29 | Change String 30 | 31 | init : Model 32 | init = 33 | Model sampleCode 34 | 35 | update : Msg -> Model -> Model 36 | update msg model = 37 | case msg of 38 | Change str -> 39 | { model | sourceCode = str} 40 | 41 | 42 | view : Model -> Html Msg 43 | view model = 44 | let 45 | content = 46 | case parse model.sourceCode of 47 | Ok file -> 48 | render file 49 | 50 | Err err -> 51 | div [] [text <| Debug.toString err] 52 | 53 | in 54 | div [style "font-family" "monospace", style "font-size" "14px", style "display" "flex", style "width" "100%", style "height" "100%"] 55 | [ div [style "width" "50%", style "padding" "10px"] [textarea [Html.Events.onInput Change, style "width" "100%", style "height" "100%"] [text model.sourceCode]] 56 | , div [style "width" "50%", style "padding" "10px", style "background-color" "#222"] [content] 57 | ] 58 | 59 | 60 | type alias Context = 61 | { line : Int 62 | , column : Int 63 | , elements : List (List Node) 64 | , currentLine : List Node 65 | } 66 | type alias Node = 67 | { tag : Maybe Token, content : String } 68 | 69 | 70 | 71 | render : File -> Html Msg 72 | render file = 73 | let 74 | tagged : Token -> String -> Context -> Context 75 | tagged tag str ctx = 76 | { ctx 77 | | column = ctx.column + String.length str 78 | , currentLine = ctx.currentLine ++ [ Node (Just tag) str ] 79 | } 80 | 81 | untagged : String -> Context -> Context 82 | untagged str ctx = 83 | { ctx 84 | | column = ctx.column + String.length str 85 | , currentLine = ctx.currentLine ++ [ Node Nothing str ] 86 | } 87 | 88 | newline : Context -> Context 89 | newline ctx = 90 | { ctx 91 | | line = ctx.line + 1 92 | , column = 0 93 | , elements = ctx.elements ++ [ ctx.currentLine ] 94 | , currentLine = [] 95 | } 96 | 97 | outer : Context -> Html msg 98 | outer ctx = 99 | let 100 | elements = 101 | ctx.elements ++ [ ctx.currentLine ] 102 | 103 | toElem node = 104 | code [ style "white-space" "pre", style "color" <| tokenColor node.tag ] [text node.content] 105 | 106 | elems = 107 | List.map 108 | (\nested -> 109 | div [ style "height" "15px" ] <| List.map toElem nested 110 | ) 111 | elements 112 | in 113 | div [] elems 114 | 115 | renderer = 116 | { init = Context 0 0 [] [] 117 | , tagged = tagged 118 | , untagged = untagged 119 | , newline = newline 120 | , outer = outer 121 | } 122 | in 123 | Elm.Pretty.prepareLayout 80 file 124 | |> Pretty.Renderer.pretty 80 renderer 125 | 126 | 127 | 128 | tokenColor : Maybe Token -> String 129 | tokenColor token = 130 | case token of 131 | Just t -> 132 | case t of 133 | Keyword -> 134 | "purple" 135 | 136 | Comment -> 137 | "green" 138 | 139 | Operator -> 140 | "gray" 141 | 142 | Type -> 143 | "lightBlue" 144 | 145 | Statement -> 146 | "white" 147 | 148 | Signature -> 149 | "pink" 150 | 151 | Literal -> 152 | "red" 153 | 154 | Number -> 155 | "orange" 156 | 157 | Nothing -> 158 | "gray" 159 | 160 | 161 | 162 | 163 | 164 | 165 | sampleCode = """module Editor exposing (main) 166 | 167 | import Html exposing (..) 168 | import Html.Attributes exposing (..) 169 | import Browser 170 | import Elm.CodeGen exposing (File) 171 | import Elm.Token exposing (Token(..)) 172 | import Elm.DSLParser exposing (parse) 173 | import Elm.Pretty exposing (pretty) 174 | import Pretty.Renderer 175 | import Parser exposing (DeadEnd) 176 | """ -------------------------------------------------------------------------------- /test/src/elm/ToFix.elm: -------------------------------------------------------------------------------- 1 | module ToFix exposing ((&&), (<=), (>=), Bool, Int, SomeType(..), blang, bling, blong, (||)) 2 | 3 | import Blah 4 | import Blah 5 | import Blah as B 6 | import Blah exposing (blong) 7 | import Blah as H exposing (blang) 8 | import Blah as B exposing (bling) 9 | import Blah exposing (SomeType(..)) 10 | import Blah exposing (SomeType,(&&),(<=),(>=),Bool,Int,(||)) 11 | 12 | 13 | -- Indentation 14 | 15 | 16 | indentation1 = 17 | 60 18 | * (if x == 2 then 19 | 7 20 | else 21 | 9) 22 | 23 | 24 | 25 | indentation2 = 26 | 60 27 | * (case x of 28 | [] -> 8 29 | 30 | _ -> 12 31 | ) 32 | 33 | 34 | indentation3 = 35 | 60 36 | * (let 37 | x = 2 38 | in 39 | x) 40 | 41 | 42 | indentation4 = 43 | fn (if x == 2 then 6 else 7) 44 | arg 45 | 46 | 47 | 48 | indentation5 = 49 | [ if x == 2 then 50 | 7 51 | else 52 | 9 53 | , if x == 2 then 54 | 7 55 | else 56 | 9 57 | , case x of 58 | [] -> 8 59 | 60 | _ -> 12 61 | , let 62 | x = 2 63 | in 64 | x 65 | , fn (if x == 2 then 6 else 7) 66 | arg 67 | , \w -> 68 | case w |> String.toLower of 69 | "get" -> 70 | Just GET 71 | ] 72 | 73 | 74 | indentation6 = 75 | fn 76 | (if x == 2 then 6 else 7) 77 | |> blah 78 | 79 | 80 | indentation7 = 81 | blah 82 | |> fn 83 | if x == 2 then 6 else 7 84 | 85 | 86 | indentation8 = 87 | fn 88 | |> List.map 89 | (\w -> 90 | case w |> String.toLower of 91 | "get" -> 92 | Just GET 93 | ) 94 | 95 | 96 | indentation9 = 97 | fn 98 | |> (case model.type_ of 99 | Ordered startInt -> 100 | if startInt == 1 then 101 | ol [] 102 | 103 | else 104 | ol [ start startInt ] 105 | 106 | Unordered -> 107 | ul [] 108 | ) 109 | |> blah 110 | 111 | 112 | indentation10 = 113 | blah 114 | |> List 115 | { model 116 | | indentLength = listBlock.indentLength 117 | , isLoose = model.isLoose || isBlankLineLast items 118 | } 119 | 120 | 121 | indentation11 = 122 | JDP.required 123 | (JD.succeed Response 124 | |> resultWrapperDecoder resultDecoder 125 | |> JDP.required "ResponseMetadata" (JD.succeed Metadata |> JDP.required "RequestId" JD.string) 126 | ) 127 | 128 | 129 | indentation12 = 130 | div 131 | ([ classList 132 | [ ( "popover fade", True ) 133 | , ( "in", isShown ) 134 | , ( "top", direction == Top ) 135 | , ( "right", direction == Right ) 136 | , ( "bottom", direction == Bottom ) 137 | , ( "left", direction == Left ) 138 | ] 139 | ] 140 | ++ styles (styleList ++ [ ( "display", "block" ) ]) 141 | ) 142 | 143 | 144 | indentation13 = 145 | ( String.toLower key 146 | , val 147 | |> Regex.replace (Regex.fromString "\\s*?\n\\s*" |> Maybe.withDefault Regex.never) (\_ -> ",") 148 | |> Regex.replace (Regex.fromString "(^\\s*|\\s*$)" |> Maybe.withDefault Regex.never) (\_ -> "") 149 | |> Regex.replace (Regex.fromString "\\s{2,}" |> Maybe.withDefault Regex.never) (\_ -> " ") 150 | ) 151 | 152 | 153 | indentation14 = 154 | JD.lazy 155 | (\() -> 156 | decodeTyped 157 | [ ( "function", Expression.functionDecoder |> JD.map FunctionDeclaration ) 158 | , ( "typeAlias", TypeAlias.decoder |> JD.map AliasDeclaration ) 159 | , ( "typedecl", Type.decoder |> JD.map CustomTypeDeclaration ) 160 | , ( "port", Signature.decoder |> JD.map PortDeclaration ) 161 | , ( "infix", Infix.decoder |> JD.map InfixDeclaration ) 162 | , ( "destructuring" 163 | , JD.map2 164 | Destructuring 165 | (JD.field "pattern" (Node.decoder Pattern.decoder)) 166 | (JD.field "expression" (Node.decoder Expression.decoder)) 167 | ) 168 | ] 169 | ) 170 | 171 | 172 | indentation15 = 173 | LowLevel.decodeFlags 174 | ( if abs deltaTheta > pi then 175 | 1 176 | 177 | else 178 | 0 179 | , if deltaTheta > 0 then 180 | 1 181 | 182 | else 183 | 0 184 | ) 185 | |> Maybe.withDefault ( SmallestArc, CounterClockwise ) 186 | 187 | 188 | indentation16 = 189 | case msg of 190 | Frame time -> 191 | ( { model 192 | | offset = 193 | let 194 | new = 195 | model.offset + time / 20 196 | 197 | size = 198 | segmentLength model.count 199 | in 200 | floatModulo new size 201 | , more = more 202 | } 203 | , Cmd.none 204 | ) 205 | 206 | 207 | 208 | indentation17 = 209 | buildSchema 210 | |> withDefinitions 211 | [ ( "schemaArray" 212 | , buildSchema |> withType "array" |> withItem (buildSchema |> withRef "#") |> withMinItems 1 213 | ) 214 | ] 215 | 216 | 217 | -- 218 | 219 | definitionsDecoder : Decoder Definitions 220 | definitionsDecoder = 221 | field 222 | "definitions" 223 | (keyValuePairs preSchemaDecoder |> map (List.map (Tuple.mapFirst ((++) "#/definitions/")) >> Dict.fromList)) 224 | |> maybe 225 | |> map (Maybe.withDefault Dict.empty) 226 | 227 | 228 | 229 | 230 | 231 | type ArcLengthParameterized 232 | = ArcLengthParameterized 233 | { underlyingSpline: QuadraticSpline2d 234 | , parameterization: ArcLengthParameterization 235 | , nondegenerateSpline: MaybeNondegenerate 236 | } 237 | 238 | 239 | stackIfs = 240 | if blah then 241 | val1 242 | 243 | else if blah then 244 | val2 245 | 246 | else 247 | val3 248 | 249 | 250 | longIf = 251 | if leftOflineSegments point && (Point2d.signedDistanceFromleftAxis point <= 0) && (Point2d.signedDistanceFromrightAxis point >= 0) then 252 | blah 253 | 254 | else 255 | blah 256 | 257 | addBrackets1 = 258 | let 259 | ( r, g, b ) = 260 | cl |> toRgb |> \a -> ( f a.red, f a.green, f a.blue ) 261 | in 262 | () 263 | 264 | 265 | noBrackets1 = 266 | (6 + 7) 267 | 268 | 269 | noBrackets2 = 270 | ((6 + 7)) 271 | 272 | 273 | noBrackets3 = 274 | (toFloat intensity) / 255 275 | 276 | 277 | noBrackets4 = 278 | f ((g h)) 279 | 280 | 281 | noBrackets5 = 282 | case blah of 283 | (Constructor c) -> blah 284 | 285 | 286 | noBrackets6 = 287 | Just 288 | <| if blah then 289 | val1 290 | 291 | else 292 | val2 293 | 294 | 295 | noBrackets7 = 296 | blah <| (\a -> b) 297 | 298 | 299 | noBrackets8 = 300 | case blah of 301 | Constructor :: tl -> blah 302 | 303 | 304 | noBrackets9 = 305 | case blah of 306 | ( (Just pid), [] ) -> blah 307 | 308 | 309 | needsBrackets1 = 310 | f (g h) 311 | 312 | 313 | needsBrackets2 = 314 | case blah of 315 | Constructor c :: tl -> blah 316 | 317 | 318 | needsBrackets3 = 319 | Just 320 | << if blah then 321 | fn1 322 | 323 | else 324 | fn2 325 | 326 | 327 | 328 | needsBrackets4 = 329 | 60 330 | * (let 331 | cMax = r 332 | in 333 | 6) 334 | 335 | 336 | needsBrackets5 = 337 | 60 338 | * (case cMax == r of 339 | _ -> 6) 340 | 341 | 342 | needsBrackets6 = 343 | blah <| (a |> b) 344 | 345 | 346 | needsBrackets7 : RTree a -> a 347 | needsBrackets7 (Node a list) = 348 | a 349 | 350 | 351 | needsBrackets8 : (a -> Bool) -> a -> RTree a -> RTree a 352 | needsBrackets8 f new tree = 353 | let 354 | (Node a list) = 355 | tree 356 | 357 | (Node a_ list_) = 358 | if f a then 359 | addChild new tree 360 | 361 | else 362 | tree 363 | in 364 | Node a_ (List.map (addChildAt f new) list_) 365 | 366 | 367 | 368 | 369 | longcase2 = 370 | case leftOflineSegments point && (Point2d.signedDistanceFromleftAxis point <= 0) && (Point2d.signedDistanceFromrightAxis point >= 0) of 371 | True -> () 372 | 373 | 374 | rightPipeEol = 375 | (funcion with lots and lots and lots and lots and lots parameters to make it nice and long) 376 | <| (funcion with lots and lots and lots and lots and lots parameters to make it nice and long) 377 | 378 | 379 | type alias Keyframes compatible = 380 | { compatible | keyframes : Compatible, none : Compatible, value : String } 381 | 382 | 383 | render : (Parts.Msg (Container c) m -> m) -> Parts.Index (List Int) -> Container c -> List (Property m) -> List (Html m) -> Html m 384 | render = 385 | () 386 | 387 | 388 | title styling block = 389 | Title 390 | (List.append 391 | styling 392 | [ css "justify-content" "flex-end", css "flex-direction" "column", css "align-items" "flex-start" ] 393 | ) 394 | block 395 | 396 | 397 | type alias HashData = 398 | { shift : Int, seed : Int, hash : Int, charsProcessed : Int } 399 | 400 | 401 | fromEndpoints : { startPoint : Point2d, startDerivative : Vector2d, endPoint : Point2d, endDerivative : Vector2d } -> CubicSpline2d 402 | fromEndpoints arguments = 403 | () 404 | 405 | parensBroken = 406 | Process.spawn (Task.andThen (Platform.sendToSelf router) rAF) 407 | |> Task.andThen 408 | (\pid -> Task.sequence (List.map send subs) |> Task.andThen (\_ -> Task.succeed (State subs (Just pid) newTime)) 409 | ) 410 | 411 | 412 | -- Literals 413 | 414 | 415 | hex = 416 | 0xAFFF 417 | 418 | 419 | tabs = " " 420 | 421 | 422 | unicode = " " 423 | 424 | 425 | unicode2 = "✓ a-ok" 426 | 427 | 428 | epsilon:Float 429 | epsilon = 430 | 1e-12 431 | -------------------------------------------------------------------------------- /test/src/elm/Top.elm: -------------------------------------------------------------------------------- 1 | port module Top exposing (main) 2 | 3 | import Elm.DSLParser 4 | import Elm.Parser 5 | import Elm.Pretty 6 | import Elm.Processing 7 | import Elm.RawFile exposing (RawFile) 8 | import Pretty 9 | 10 | 11 | 12 | -- Top level construction 13 | 14 | 15 | main : Program () Model Msg 16 | main = 17 | Platform.worker { init = init, update = update, subscriptions = subscriptions } 18 | 19 | 20 | 21 | -- Ports for data input and output 22 | 23 | 24 | port modelInPort : (( String, String ) -> msg) -> Sub msg 25 | 26 | 27 | port codeOutPort : ( String, String ) -> Cmd msg 28 | 29 | 30 | subscriptions model = 31 | case model of 32 | Error -> 33 | Sub.none 34 | 35 | _ -> 36 | Sub.batch 37 | [ modelInPort (\( name, value ) -> ModelData name value) 38 | ] 39 | 40 | 41 | 42 | -- State Machine 43 | 44 | 45 | type Model 46 | = Error 47 | | Normal 48 | 49 | 50 | 51 | -- Events 52 | 53 | 54 | type Msg 55 | = ModelData String String 56 | 57 | 58 | init _ = 59 | ( Normal, Cmd.none ) 60 | 61 | 62 | update msg model = 63 | case ( model, msg ) of 64 | ( Normal, ModelData name val ) -> 65 | let 66 | elmAstResult = 67 | Elm.DSLParser.parse val 68 | in 69 | case elmAstResult of 70 | Err _ -> 71 | let 72 | _ = 73 | Debug.log "error" name 74 | in 75 | ( Normal, Cmd.none ) 76 | 77 | Ok file -> 78 | let 79 | pretty = 80 | Elm.Pretty.pretty 120 file 81 | in 82 | ( model, codeOutPort ( name, pretty ) ) 83 | 84 | ( _, _ ) -> 85 | ( model, Cmd.none ) 86 | -------------------------------------------------------------------------------- /test/src/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var glob = require('glob'); 3 | var path = require('path'); 4 | const { exec } = require('child_process'); 5 | 6 | //const { Elm } = require('./Top.elm'); -- do it this way if using webpack-loader 7 | const { 8 | Elm 9 | } = require('./elm.js'); 10 | 11 | const app = Elm.Top.init(); 12 | 13 | glob("examples/*.elm", function(er, files) { 14 | files.forEach(function(file) { 15 | fs.readFile(file, 'utf8', function(err, contents) { 16 | var filename = path.basename(file); 17 | app.ports.modelInPort.send([filename, contents]); 18 | }); 19 | }); 20 | }); 21 | 22 | app.ports.codeOutPort.subscribe(request => { 23 | var filename = request[0]; 24 | var contents = request[1]; 25 | 26 | fs.writeFile('pre/' + filename, contents, (err) => { 27 | if (err) throw err; 28 | 29 | exec('"elm-format" pre/' + filename + ' --output post/' + filename); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /tests/Tests.elm: -------------------------------------------------------------------------------- 1 | module Tests exposing (tests) 2 | 3 | import Elm.CodeGen 4 | import Elm.Pretty 5 | import Expect 6 | import Pretty 7 | import Test exposing (Test) 8 | 9 | 10 | tests : Test 11 | tests = 12 | Test.describe "escape sequence in literal characters sometimes isn't escaped in pretty printed expression" 13 | [ Test.test "string with escaped line break is pretty printed correctly" 14 | (\() -> 15 | Elm.CodeGen.string "\n" 16 | |> Elm.Pretty.prettyExpression 17 | |> Pretty.pretty 100 18 | |> Expect.equal "\"\\n\"" 19 | ) 20 | , Test.test "string with escaped tab break is pretty printed correctly" 21 | (\() -> 22 | Elm.CodeGen.string "\t" 23 | |> Elm.Pretty.prettyExpression 24 | |> Pretty.pretty 100 25 | |> Expect.equal "\"\\t\"" 26 | ) 27 | , Test.test "string with escaped backslash return is pretty printed correctly" 28 | (\() -> 29 | Elm.CodeGen.string "\\" 30 | |> Elm.Pretty.prettyExpression 31 | |> Pretty.pretty 100 32 | |> Expect.equal "\"\\\\\"" 33 | ) 34 | , Test.test "string with escaped double quote is pretty printed correctly" 35 | (\() -> 36 | Elm.CodeGen.string "\"" 37 | |> Elm.Pretty.prettyExpression 38 | |> Pretty.pretty 100 39 | |> Expect.equal "\"\\\"\"" 40 | ) 41 | , Test.test "string with escaped carriage return is pretty printed correctly" 42 | (\() -> 43 | Elm.CodeGen.string "\u{000D}" 44 | |> Elm.Pretty.prettyExpression 45 | |> Pretty.pretty 100 46 | |> Expect.equal "\"\\u{000D}\"" 47 | ) 48 | , Test.test "string with escaped combo emoji is pretty printed correctly" 49 | (\() -> 50 | Elm.CodeGen.string "👩\u{200D}🚒" 51 | |> Elm.Pretty.prettyExpression 52 | |> Pretty.pretty 100 53 | |> String.toList 54 | |> Expect.equalLists ("\"👩\\u{200D}🚒\"" |> String.toList) 55 | ) 56 | , Test.test "char with escaped line break is pretty printed correctly" 57 | (\() -> 58 | Elm.CodeGen.char '\n' 59 | |> Elm.Pretty.prettyExpression 60 | |> Pretty.pretty 100 61 | |> Expect.equal "'\\n'" 62 | ) 63 | , Test.test "char with escaped tab is pretty printed correctly" 64 | (\() -> 65 | Elm.CodeGen.char '\t' 66 | |> Elm.Pretty.prettyExpression 67 | |> Pretty.pretty 100 68 | |> Expect.equal "'\\t'" 69 | ) 70 | , Test.test "char with escaped backslash is pretty printed correctly" 71 | (\() -> 72 | Elm.CodeGen.char '\\' 73 | |> Elm.Pretty.prettyExpression 74 | |> Pretty.pretty 100 75 | |> Expect.equal "'\\\\'" 76 | ) 77 | , Test.test "char with escaped single quote is pretty printed correctly" 78 | (\() -> 79 | Elm.CodeGen.char '\'' 80 | |> Elm.Pretty.prettyExpression 81 | |> Pretty.pretty 100 82 | |> Expect.equal "'\\''" 83 | ) 84 | , Test.test "char with escaped double quote is pretty printed correctly" 85 | (\() -> 86 | Elm.CodeGen.char '"' 87 | |> Elm.Pretty.prettyExpression 88 | |> Pretty.pretty 100 89 | |> Expect.equal "'\"'" 90 | ) 91 | , Test.test "char with escaped code 200D is pretty printed correctly" 92 | (\() -> 93 | Elm.CodeGen.char '\u{200D}' 94 | |> Elm.Pretty.prettyExpression 95 | |> Pretty.pretty 100 96 | |> Expect.equal "'\\u{200D}'" 97 | ) 98 | , Test.test "char with escaped carriage return is pretty printed correctly" 99 | (\() -> 100 | Elm.CodeGen.char '\u{000D}' 101 | |> Elm.Pretty.prettyExpression 102 | |> Pretty.pretty 100 103 | |> Expect.equal "'\\u{000D}'" 104 | ) 105 | ] 106 | --------------------------------------------------------------------------------