├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── coffeelint.json ├── grammars ├── c2hs.cson ├── cabal.cson ├── haskell autocompletion hint.cson ├── haskell message hint.cson ├── haskell type hint.cson ├── haskell.cson ├── hsc2hs.cson ├── liquid haskell.cson ├── literate haskell.cson └── module signature.cson ├── package-lock.json ├── package.json ├── settings └── language-haskell.cson ├── snippets └── language-haskell.cson ├── spec ├── fixture │ ├── charKind.hs │ ├── deriveStrategy.hs │ ├── gadt.hs │ ├── general.hs │ ├── identifiers.hs │ ├── liquidhaskell.hs │ ├── module-exports.hs │ ├── multilineHaddock.hs │ ├── multilineSignatures.hs │ ├── patternsTest.hs │ ├── pragmasTest.hs │ ├── record.hs │ ├── signatures.hs │ ├── type-app.hs │ ├── type-export.hs │ └── typeFamilies.hs ├── grammar-test-spec.coffee ├── language-haskell-multiline-sigs-spec.coffee ├── language-haskell-numbers-spec.coffee ├── language-haskell-spec.coffee ├── literate-haskell-spec.coffee ├── operators-spec.coffee ├── snippets-spec.coffee └── util.coffee ├── src ├── haskell.coffee ├── include │ ├── haskell-patterns.coffee │ ├── hsig-patterns.coffee │ ├── lhs-patterns.coffee │ ├── liquid-patterns.coffee │ ├── macros.coffee │ ├── pragmas.coffee │ ├── prelude.coffee │ ├── repository.coffee │ └── util.coffee ├── makeprelude.coffee └── syntax-tools.coffee └── styles └── overrides.less /.gitattributes: -------------------------------------------------------------------------------- 1 | grammars/haskell.cson linguist-generated 2 | grammars/module[[:space:]]signature.cson linguist-generated 3 | grammars/haskell[[:space:]]autocompletion hint.cson linguist-generated 4 | grammars/haskell[[:space:]]type[[:space:]]hint.cson linguist-generated 5 | grammars/haskell[[:space:]]message[[:space:]]hint.cson linguist-generated 6 | grammars/literate[[:space:]]haskell.cson linguist-generated 7 | grammars/liquid[[:space:]]haskell.cson linguist-generated 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [master] 10 | pull_request: 11 | branches: [master] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | Test: 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: [ubuntu-latest] 20 | channel: [stable, beta] 21 | runs-on: ${{ matrix.os }} 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: UziTech/action-setup-atom@v2 25 | with: 26 | version: ${{ matrix.channel }} 27 | - name: Atom version 28 | run: atom -v 29 | - name: APM version 30 | run: apm -v 31 | - name: Install dependencies 32 | run: apm ci 33 | - name: Build required artifacts 34 | run: make 35 | - name: Run tests 36 | shell: bash 37 | run: atom --no-sandbox --test spec 38 | - name: Check for changed files 39 | shell: bash 40 | run: git diff --quiet -- dist/ 41 | - name: Check for new files 42 | shell: bash 43 | run: | 44 | untracked="$(git ls-files --others --exclude-standard)" && echo "$untracked" && test -z "$untracked" 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.24.0 2 | 3 | - Recognize `COLUMN` and `COMPLETE` pragmas 4 | - Recognize `DEPRECATED` pragmas module declarations 5 | - Recognize `pattern` keyword 6 | - Recognize explicit namespacing in module exports/imports 7 | - Include pragma name into its scope name, e.g. `keyword.other.preprocessor.OVERLAPPING.haskell` 8 | 9 | ## 1.23.0 10 | 11 | - Tweak promoted type highlighting; fix char\/promoted conflict 12 | - Visible type applications 13 | 14 | ## 1.22.1 15 | 16 | - Override questionable type kewyord highlighting 17 | 18 | Atom 1.56 changed default highlighting rules, mostly borrowing from C, many which don't make much sense in Haskell. Instead of changing scopes (which would be a breaking change), I've added some overrides to restore the previous behaviour for Haskell specifically. Will be extending that as needed, feel free to open issues if something highlights weird. 19 | 20 | ## 1.22.0 21 | 22 | - Add Char kind support 23 | - Add Char kind test 24 | 25 | ## 1.21.0 26 | 27 | - Disable snippet spec 28 | - Detect .hs-boot and cabal.project\/cabal.project.local file types (Alexis King) 29 | 30 | ## 1.20.0 31 | 32 | - Mark quasiquotes with the function-based class 33 | 34 | Please see [#88](https://github.com/atom-haskell/language-haskell/issues/88) for context. 35 | 36 | ## 1.19.4 37 | 38 | - Improve highlighting multiline via statements 39 | 40 | ## 1.19.3 41 | 42 | - Fix breakage reported in #126; improve via syntax a little 43 | 44 | ## 1.19.2 45 | 46 | - Fix #125 47 | 48 | A curious case of `module Test ( (:-)(), apply ) where` not parsing 49 | correctly due to `()` being parsed as `(` and then `)` closing the 50 | exports list. This is fixed by relaxing the syntax definition for the 51 | constructor list exports. 52 | 53 | - Forbid (..) operator 54 | 55 | (..) user-defined operator is forbidden in Haskell, since according to 56 | Haskell2010 report, it's one of the reserved operators: 57 | `..`,`:`,`::`,`=`,`\`,`|`,`<-`,`->`,`@`,`~`,`=>`. 58 | In particular, `..` clashes with explicit wildcard export, e.g. 59 | `module Something ( Typename(..) ) where`, hence `(..)` should never be 60 | parsed as an operator. 61 | 62 | ## 1.19.1 63 | 64 | - Fix existential quantification in data declarations 65 | - Add test for multiline type ctor defns 66 | 67 | ## 1.19.0 68 | 69 | - Rework data declarations to support multiline constructor definitions 70 | 71 | ## 1.18.2 72 | 73 | - cabal.cson: support common stanza (#122) (Domen Kožar) 74 | 75 | ## 1.18.1 76 | 77 | - Fix standalone deriving via 78 | 79 | ## 1.18.0 80 | 81 | - Initial support for deriving strategies 82 | 83 | ## 1.17.8 84 | 85 | - Fix infix type constructor exports 86 | 87 | ## 1.17.7 88 | 89 | - Rework scoped types 90 | - Fix data declarations comments 91 | 92 | ## 1.17.6 93 | 94 | - Add module name highlighting to functions 95 | 96 | ## 1.17.5 97 | 98 | - Fix comments containing <- and = after function signatures 99 | 100 | ## 1.17.4 101 | 102 | - Update\/fix specs 103 | - Remove string scope from quasiquotes (URI injection breaks highlighting) 104 | 105 | ## 1.17.3 106 | * Better module exports highlighting at the cost of C 107 | 108 | Basically, highlight CPP inside module export definitions. That comes with a caveat of not using embedded C grammar for CPP anymore. See 78810e0 for a more detailed explanation 109 | 110 | ## 1.17.2 111 | * Handle promoted types 112 | 113 | ```haskell 114 | type family TF a where 115 | TF 'Nothing = 0 116 | TF ('Just a) = 1 117 | ``` 118 | * Added keyword name to scope name 119 | 120 | example (for `if` and `deriving` keywords): 121 | 122 | - `keyword.control.haskell` → `keyword.control.if.haskell` 123 | - `keyword.other.haskell` → `keyword.other.deriving.haskell` 124 | * Removed redundant keyword-related rules 125 | * More specs, better specs 126 | 127 | ## 1.17.1 128 | * Terminate function signatures on (<-|=) 129 | * Add some general tests 130 | * Add expressions and comments to liquidhaskell 131 | * Add fixture-based test 132 | 133 | ## 1.17.0 134 | * Support standalone multiline type signatures 135 | ```haskell 136 | someFunction 137 | :: Type 138 | -> OtherType 139 | ``` 140 | 141 | ## 1.16.2 142 | * Fix README formatting error 143 | 144 | ## 1.16.1 145 | * Remove usage information; link to doc site instead 146 | 147 | ## 1.16.0 148 | * Parse qualified prefix for operators 149 | * Mark prelude operators with `support.operator.prelude.haskell` 150 | * **POSSIBLE COMPATIBILITY ISSUE** Rename prefix operator application to better describe it.\ 151 | `entity.name.function.infix.haskell` scope renamed to `entity.name.function.operator.haskell` 152 | * Prelude updated; added operators 153 | 154 | ## 1.15.0 155 | * Tests for number literals 156 | * Patch numeric underscores (#112) (Takenobu Tani) 157 | 158 | ## 1.14.3 159 | * Fix qualified quasiqotes (#111) 160 | * Updated contributors list 161 | 162 | ## 1.14.2 163 | * Do not attempt to match newline characters -- Atom 1.22+ compatibility (@Wliu) 164 | 165 | ## 1.14.1 166 | * Rudimentary support for LH annotation keywords 167 | 168 | ## 1.14.0 169 | * Basic support for liquid types 170 | * Rework LiquidHaskell (avoid scope leaks) 171 | 172 | ## 1.13.3 173 | * Fix mixed-case pragmas (#109) 174 | 175 | ## 1.13.2 176 | * Add "lazy" function signatures to hsig (works if :: is on next line) 177 | * Update CI script 178 | 179 | ## 1.13.1 180 | * Added comments to hsig top level 181 | 182 | ## 1.13.0 183 | * Added hsig grammar; fixed operatorChar in lhs 184 | 185 | ## 1.12.1 186 | * Fix comments in imports (now before ()) 187 | 188 | ## 1.12.0 189 | * Readme update 190 | * Better handling of prelude identifiers 191 | * Revert "Add scope tags to Prelude identifiers" 192 | 193 | ## 1.11.0 194 | * Finally fix snippets dependency (hopefully) 195 | * Install snippets package in travis tests 196 | * Specs update 197 | * Add scope tags to Prelude identifiers 198 | 199 | ## 1.10.6 200 | * Add mdo, rec, proc keywords 201 | * Add travis tests 202 | 203 | ## 1.10.5 204 | * Add cpphs extension to Haskell grammar 205 | 206 | ## 1.10.4 207 | * Make operatorChar PCRE-compatible 208 | * Add applyEndPatternLast to module_exports 209 | * Module spec 210 | 211 | ## 1.10.3 212 | * Use snippets parser in snippets spec 213 | * Add snippets spec 214 | * Fix snippet scopes, tweak some snippets 215 | * Fix record spec 216 | * Remove debug line from spec 217 | * Add more snippets 218 | * Update CONTRIBUTING.md 219 | * Fix typo 220 | * Fix contributing link 221 | 222 | ## 1.10.2 223 | * Remove scope for comment leading whitespace (#99) 224 | * Add contributing reference in readme 225 | * Added CONTRIBUTING 226 | 227 | ## 1.10.1 228 | * Add support for type-level string literals 229 | * Fix flaky control character pattern 230 | 231 | ## 1.10.0 232 | * Change storage.type scope to keyword.other 233 | * Fix LICENSE date 234 | * Update LICENSE 235 | 236 | ## 1.9.12 237 | * Fix module/class/instance end anchors 238 | 239 | ## 1.9.11 240 | * Fix anchoring for identifiers'with'apostrophes in the middle 241 | 242 | ## 1.9.10 243 | * Support for StandaloneDeriving 244 | * Clean foreign imp/exp 245 | * Add spec for quoted '::' 246 | * Simplify some specs 247 | * Fix for scoped_type with :: in string 248 | 249 | ## 1.9.9 250 | * Now really fix #64 251 | 252 | ## 1.9.8 253 | * Fix #64... again 254 | 255 | ## 1.9.7 256 | * Add prelude scope to support.tag 257 | * Readme update 258 | 259 | ## 1.9.6 260 | * Move prelude rules to related scopes 261 | * Use proper right boundary for type alias 262 | * Add most (hopefully all) recognized GHC pragmas 263 | * instance spec 264 | 265 | ## 1.9.5 266 | * Fix for #84 267 | * Specs for #84 268 | 269 | ## 1.9.4 270 | * Fix for #82 271 | * Tests for #82 272 | * Custom jasmine matchers 273 | 274 | ## 1.9.3 275 | * Tweaks and fixes to scoped operators 276 | * Fix type operators/arrows conflict 277 | 278 | ## 1.9.2 279 | * Type operators, (partial) fix for #71 280 | 281 | ## 1.9.1 282 | * Fix data declaration highlighting 283 | * Add data decl spec 284 | * Remove debug print 285 | * Removed obsolete macro,ident arg to util.list func 286 | 287 | ## 1.9.0 288 | * Highlight INCOHERENT/OVERLAP* instance pragmas 289 | * Properly highlight related imports/exports (e.g. Class(..) or Record(field)) 290 | * Fix comments in record declarations 291 | * Clean-up type signature patterns 292 | 293 | Notes: 294 | 295 | I've made a hard decision to stop trying to highlight arbitrary type 296 | constraints. Only prelude classes are highlighted as constraints 297 | now. 298 | 299 | Class constraints were a source of major head-ache for a while now, 300 | and in many cases didn't work very well. 301 | 302 | ## 1.8.3 303 | * Hide hint.* grammars from Atom UI 304 | 305 | ## 1.8.2 306 | * Some fixes to class constraints 307 | 308 | ## 1.8.1 309 | * Fix multiparam class constraints, forall 310 | 311 | ## 1.8.0 312 | * Better highlighting for type families 313 | * GADTs unified with `data` definitons 314 | * Simplified many regexes to avoid unexpected backtracking-related hangs 315 | * Most haskell grammar patterns moved to repository 316 | * Fix GADT ctor scope to `tag` 317 | * Grammar files are distributed instead of being generated now 318 | * Remove hack used for grammar hooks 319 | 320 | ## 1.7.17 321 | * Prelude typeclasses 322 | * Merge branch 'master' of github.com:atom-haskell/language-haskell 323 | * Grab prelude definitions from Prelude 324 | 325 | ## 1.7.16 326 | * Fix #76 327 | 328 | ## 1.7.15 329 | * Another stab at Windows postinstall 330 | 331 | ## 1.7.14 332 | * Update postinstall hook to hopefully make it work in windows 333 | * Spec update 334 | * Various tweaks to word anchoring, symbol provider 335 | 336 | ## 1.7.13 337 | * Fix runaway module name 338 | 339 | ## 1.7.12 340 | * Spec update 341 | * Capture identifiers before type ctors 342 | 343 | ## 1.7.11 344 | * Fix CPP # confusion 345 | * Updated contributors 346 | 347 | ## 1.7.10 348 | * added liquidhaskell comment block (PR #69 by @ranjitjhala) 349 | 350 | ## 1.7.9 351 | * Add 'type instance' keyword 352 | 353 | ## 1.7.8 354 | * Initial support for type families 355 | 356 | ## 1.7.7 357 | * Regression tests for #65 358 | * Tweak ctor regex 359 | * Spec update 360 | 361 | ## 1.7.6 362 | * More preprocessor pragmas 363 | 364 | ## 1.7.5 365 | * Add [] () to inline type signature characters 366 | 367 | ## 1.7.4 368 | * More accurate inline type signature match 369 | * Add meta.type-signature.haskell to in-line sigs 370 | 371 | ## 1.7.3 372 | * Fix message highlighting 373 | * Add coffeelint 374 | 375 | ## 1.7.2 376 | * Merge pull request #63 from robrix/options-ghc-pragma 377 | * Add OPTIONS_GHC to the recognized pragmas. 378 | 379 | ## 1.7.1 380 | * Fix string tokenization (+tests) 381 | * In-line type signatures 382 | 383 | ## 1.7.0 384 | * Haskell message hint grammar 385 | 386 | ## 1.6.0 387 | * Haskell Type Hint grammar 388 | 389 | ## 1.5.2 390 | * Don't confuse timecop 391 | 392 | ## 1.5.1 393 | * Support multiline module export ctors (#60) 394 | 395 | ## 1.5.0 396 | * Haskell Autocompletion Hint grammar 397 | 398 | ## 1.4.12 399 | * Fix #58 (escaped quotes in multiline strings) 400 | 401 | ## 1.4.11 402 | * Fix where anchoring in typeDecl and GADT 403 | 404 | ## 1.4.10 405 | * Fix catastrophic backtracking in GADTs (#55) 406 | 407 | ## 1.4.9 408 | * Hack to trigger activation hook after all packages loaded 409 | 410 | ## 1.4.8 411 | * Amend quote escapes fix 412 | 413 | ## 1.4.7 414 | * Quote escapes fix (#53) 415 | 416 | ## 1.4.6 417 | * Word anchoring and tick (') handling (#52) 418 | 419 | ## 1.4.5 420 | * Fix double quote escape in strings (#51) 421 | 422 | ## 1.4.4 423 | * Fix gadt and record syntax conflict (#50) 424 | 425 | ## 1.4.3 426 | * Add character patterns to in-line quoted string (#47) 427 | 428 | ## 1.4.2 429 | * Quasi-fix for #47 430 | 431 | ## 1.4.1 432 | * Pipe escapes for LHS (#45) 433 | 434 | ## 1.4.0 435 | * Match scripts with `runhaskell` shebang as Haskell files 436 | * Add highlighting rule for shebang 437 | 438 | ## 1.3.1 439 | * Add `benchmark, flag, source-repository, test-suite` sections highlighting for .cabal files (by @ianbollinger) 440 | 441 | ## 1.3.0 442 | * GADTs where-syntax support (#43) 443 | 444 | ## 1.2.1 445 | * Add CHANGELOG 446 | * Fix indent block problems with data, newtype and type (#42) 447 | 448 | ## 1.2.0 449 | * Initial support for c2hs and hsc2hs 450 | * Better C preprocessor support (referencing source.c) 451 | 452 | ## 1.1.11 453 | * README: Add info on autoindent customization. 454 | * Fix increaseIndentPattern 455 | 456 | ## 1.1.10 457 | * Remove `decreaseIndentPattern` for now 458 | 459 | ## 1.1.9 460 | * Add support for `<` (reverse bird tracks) 461 | 462 | ## 1.1.8 463 | * Build grammars in postinstall hook 464 | * Add support for spec env in lit. hs. 465 | 466 | ## 1.1.7 467 | * Fix single-line comment line anchoring 468 | 469 | ## 1.1.6 470 | * Quasi-quotes support 471 | 472 | ## 1.1.5 473 | * Better lhs grammar 474 | 475 | ## 1.1.4 476 | * Update grammar 477 | * Type ctor word-anchoring 478 | * Fix for module_name 479 | 480 | ## 1.1.3 481 | * Fixes word-anchoring and priority for qualified/as/hiding 482 | 483 | ## 1.1.2 484 | * Allow module name to end with dot (for autocompletion) 485 | * Haddock snippets for all comments 486 | * Create LICENSE.md 487 | * Update README.md 488 | 489 | ## 1.1.1 490 | * Foreign import/export 491 | * Haddock comments 492 | 493 | ## 1.1.0 494 | 495 | ## 1.0.2 496 | * Merge pull request #31 from atom-haskell/master 497 | * Remove deriving from type alias 498 | * Simplify and data/newtype/record syntax 499 | * Extend ctor args, empty line doesn't count as ind. block end 500 | * Data declaration comments fix, added comments to export list 501 | * Grammar update 502 | * Minor fixes 503 | * Allow qualified fn and cn 504 | * Fix ident block end regex 505 | * Fix bugs with type_signature looping 506 | * Fix meta.declaration.type.data.record.haskell gobble 507 | * Snippets updates and fixes 508 | * deriving fix, some semantics for type/newtype/data 509 | * type/newtype/data declarations 510 | * More semantic naming 511 | * Record field declaration syntax 512 | * Haskell grammar fixes and cleanup 513 | * Added snippets from ide-haskell 514 | * Added UnicodeSyntax snippets 515 | * Snippets cleanup 516 | * Atom API 1.0 deprecation fixes 517 | * Merge pull request #11 from MichaelRawson/master (Jared Roesch) 518 | * fixed "module" keyword word-anchoring (Michael Rawson) 519 | 520 | ## 1.0.0 521 | * Merge pull request #9 from mdgriffith/master (Jared Roesch) 522 | * Merge pull request #8 from RossOgilvie/patch-1 (Jared Roesch) 523 | * Cabal Comment Toggling (Matthew Griffith) 524 | * Unnecessary word (Matthew Griffith) 525 | * Added support for Cabal files (Matthew Griffith) 526 | * Added support for the UnicodeSyntax extention. (Ross Ogilvie) 527 | 528 | ## 0.4.0 529 | * Merge pull request #6 from samuela/fix-lambda-snippet (Jared Roesch) 530 | * Merge pull request #5 from samuela/remove-type-sequence (Jared Roesch) 531 | * Fix "Lambda Expression" snippet (samuela) 532 | * Remove broken "-" snippet (samuela) 533 | 534 | ## 0.3.0 535 | * Merge pull request #3 from mdgriffith/patch-1 (Jared Roesch) 536 | * Comment symbol to allow toggling comments (mdgriffith) 537 | 538 | ## 0.2.0 539 | * remove extraneous the (Jared Roesch) 540 | 541 | ## 0.1.1 542 | * Merge pull request #1 from jc00ke/patch-1 (Jared Roesch) 543 | * Haskell, right? (Jesse Cooke) 544 | 545 | ## 0.1.0 546 | * update README.md with useful info (Jared Roesch) 547 | * Initial commit converted from https://github.com/textmate/haskell.tmbundle (Jared Roesch) 548 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # ***DO NOT EDIT GRAMMAR CSON FILES DIRECTLY*** 2 | 3 | Some grammar files are generated based on common template. In particular, 4 | 5 | - `grammars/haskell.cson` 6 | - `grammars/haskell autocompletion hint.cson` 7 | - `grammars/haskell type hint.cson` 8 | - `grammars/haskell message hint.cson` 9 | - `grammars/literate haskell.cson` 10 | 11 | are generated based on instructions in `src/haskell.coffee`. 12 | 13 | If you want to change those grammars, edit files in `src/include/` and then run `make` in the root of repository. 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Atom-Haskell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | NODE_ENV="dev" apm install 3 | node ./node_modules/coffee-script/bin/coffee src/haskell.coffee 4 | 5 | prelude: 6 | node ./node_modules/coffee-script/bin/coffee src/makeprelude.coffee 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Haskell support in Atom [![](https://travis-ci.org/atom-haskell/language-haskell.svg?branch=master)](https://travis-ci.org/atom-haskell/language-haskell) 2 | 3 | Adds syntax highlighting and snippets to Haskell files in Atom. 4 | 5 | Grammars: 6 | 7 | * Haskell (\*.hs) 8 | * Literate Haskell (\*.lhs) 9 | * Cabal (\*.cabal) 10 | 11 | ![image](https://cloud.githubusercontent.com/assets/7275622/8120540/f16d7ee6-10a8-11e5-9b9d-223ff05a54c6.png) 12 | 13 | Based on [Haskell TextMate bundle](https://github.com/textmate/haskell.tmbundle). 14 | 15 | # Usage and configuration 16 | 17 | Usage and configuration instructions available at [Atom-Haskell documentation site](https://atom-haskell.github.io/core-packages/language-haskell/) 18 | 19 | # Contributing 20 | 21 | See [CONTRIBUTING.md](https://github.com/atom-haskell/language-haskell/blob/master/CONTRIBUTING.md) 22 | 23 | # License 24 | 25 | Copyright © 2015 Atom-Haskell 26 | 27 | Contributors (by number of commits): 28 | 29 | 30 | - Nikolay Yakimov 31 | - Jared Roesch 32 | - Matthew Griffith 33 | - samuela 34 | - Wliu 35 | - Takenobu Tani 36 | - Ross Ogilvie 37 | - Rob Rix 38 | - Ranjit Jhala 39 | - Michael Rawson 40 | - mdgriffith 41 | - Jesse Cooke 42 | - Ian D. Bollinger 43 | - Domen Kožar 44 | - Alexis King 45 | 46 | 47 | 48 | See the [LICENSE.md][LICENSE] for details. 49 | 50 | [LICENSE]: https://github.com/atom-haskell/language-haskell/blob/master/LICENSE.md 51 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrow_spacing": { 3 | "name": "arrow_spacing", 4 | "level": "error" 5 | }, 6 | "ensure_comprehensions": { 7 | "name": "ensure_comprehensions", 8 | "level": "error" 9 | }, 10 | "max_line_length": { 11 | "name": "max_line_length", 12 | "value": 120, 13 | "level": "error", 14 | "limitComments": true 15 | }, 16 | "indentation": { 17 | "name": "indentation", 18 | "value": 2, 19 | "level": "error" 20 | }, 21 | "no_empty_param_list": { 22 | "name": "no_empty_param_list", 23 | "level": "error" 24 | }, 25 | "cyclomatic_complexity": { 26 | "name": "cyclomatic_complexity", 27 | "value": 22, 28 | "level": "error" 29 | }, 30 | "no_unnecessary_fat_arrows": { 31 | "name": "no_unnecessary_fat_arrows", 32 | "level": "error" 33 | }, 34 | "space_operators": { 35 | "name": "space_operators", 36 | "level": "error" 37 | }, 38 | "spacing_after_comma": { 39 | "name": "spacing_after_comma", 40 | "level": "error" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /grammars/c2hs.cson: -------------------------------------------------------------------------------- 1 | fileTypes: [ 'chs' ] 2 | name: 'C2Hs' 3 | scopeName: 'source.c2hs' 4 | patterns: [ 5 | begin: '\\{#' 6 | end: '#\\}' 7 | beginCaptures: 8 | 0: name: 'punctuation.definition.preprocessor.begin.c2hs' 9 | endCaptures: 10 | 0: name: 'punctuation.definition.preprocessor.end.c2hs' 11 | name: 'meta.preprocessor.c2hs' 12 | , 13 | include: 'source.haskell' 14 | ] 15 | -------------------------------------------------------------------------------- /grammars/cabal.cson: -------------------------------------------------------------------------------- 1 | 'scopeName': 'source.cabal' 2 | 'fileTypes': [ 3 | 'cabal' 4 | 'cabal.project' 5 | 'cabal.project.local' 6 | ] 7 | 'name': 'Cabal' 8 | 'patterns': [ 9 | 10 | { 11 | 'match':'(version)\\W*:\\W*([\\d\.]+)' 12 | 'captures': 13 | '1': 14 | 'name':'keyword.other' 15 | '2': 16 | 'name':'constant.numeric' 17 | 'name':'version' 18 | } 19 | { 20 | 'match':'(\\S+):[^/]' 21 | 'captures': 22 | '1': 23 | 'name':'keyword.other' 24 | 'name':'cabal-keyword' 25 | } 26 | 27 | { 28 | 'match':'&&' 29 | 'name':'keyword.other' 30 | } 31 | { 32 | 'match':'([><=]+)\\s*([\\d\.]+)' 33 | 'captures': 34 | '1': 35 | 'name':'keyword.other' 36 | '2': 37 | 'name':'constant.numeric' 38 | 39 | 'name':'cabal-keyword' 40 | 41 | } 42 | 43 | { 44 | 'match': '(benchmark|common|executable|flag|source-repository|test-suite)\\s+(\\S+)' 45 | 'captures': 46 | '1': 47 | 'name':'entity.name.function' 48 | '2': 49 | 'name':'support.other' 50 | 'name':'module-type' 51 | } 52 | { 53 | 'match':'library' 54 | 'name':'entity.name.function' 55 | } 56 | { 57 | 'match':'--.*\\n' 58 | 'name':'comment' 59 | } 60 | ] 61 | -------------------------------------------------------------------------------- /grammars/hsc2hs.cson: -------------------------------------------------------------------------------- 1 | fileTypes: [ 'hsc' ] 2 | name: 'Hsc2Hs' 3 | scopeName: 'source.hsc2hs' 4 | patterns: [ 5 | include: 'source.haskell' 6 | ] 7 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "language-haskell", 3 | "version": "1.24.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "atom-grammar-test": { 8 | "version": "0.6.4", 9 | "resolved": "https://registry.npmjs.org/atom-grammar-test/-/atom-grammar-test-0.6.4.tgz", 10 | "integrity": "sha1-2KU1A9H+k5mX9Ji3SirDEARKfU4=", 11 | "dev": true, 12 | "requires": { 13 | "chevrotain": "^0.18.0", 14 | "escape-string-regexp": "^1.0.5" 15 | } 16 | }, 17 | "chevrotain": { 18 | "version": "0.18.0", 19 | "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-0.18.0.tgz", 20 | "integrity": "sha1-sodxTjFZC64sXR4vYRZz7+xHnYA=", 21 | "dev": true 22 | }, 23 | "clone": { 24 | "version": "1.0.2", 25 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", 26 | "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", 27 | "dev": true 28 | }, 29 | "coffee-script": { 30 | "version": "1.9.3", 31 | "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.9.3.tgz", 32 | "integrity": "sha1-WW5ug/z8tnxZZKtw1ES+/wrASsc=", 33 | "dev": true 34 | }, 35 | "escape-string-regexp": { 36 | "version": "1.0.5", 37 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 38 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 39 | "dev": true 40 | }, 41 | "season": { 42 | "version": "2.0.0", 43 | "resolved": "https://registry.npmjs.org/season/-/season-2.0.0.tgz", 44 | "integrity": "sha1-EKjXvO4an/ji/qcCji7cfJGNK30=", 45 | "dev": true, 46 | "requires": { 47 | "cson-safe": "~0.1.1", 48 | "fs-plus": "2.x", 49 | "optimist": "~0.4.0", 50 | "underscore-plus": "1.x" 51 | }, 52 | "dependencies": { 53 | "cson-safe": { 54 | "version": "0.1.1", 55 | "resolved": "https://registry.npmjs.org/cson-safe/-/cson-safe-0.1.1.tgz", 56 | "integrity": "sha1-XFa4BC2UhCqTIg+mWn81f83gwnM=", 57 | "dev": true, 58 | "requires": { 59 | "coffee-script-redux": "2.0.0-beta8" 60 | }, 61 | "dependencies": { 62 | "coffee-script-redux": { 63 | "version": "2.0.0-beta8", 64 | "resolved": "https://registry.npmjs.org/coffee-script-redux/-/coffee-script-redux-2.0.0-beta8.tgz", 65 | "integrity": "sha1-D9e4QXNA3Q0zno9v2LS4cWlW6NU=", 66 | "dev": true, 67 | "requires": { 68 | "StringScanner": "~0.0.3", 69 | "cscodegen": "git://github.com/michaelficarra/cscodegen.git#73fd7202ac086c26f18c9d56f025b18b3c6f5383", 70 | "escodegen": "~0.0.24", 71 | "esmangle": "~0.0.8", 72 | "nopt": "~2.1.2", 73 | "source-map": "0.1.11" 74 | }, 75 | "dependencies": { 76 | "StringScanner": { 77 | "version": "0.0.3", 78 | "resolved": "https://registry.npmjs.org/StringScanner/-/StringScanner-0.0.3.tgz", 79 | "integrity": "sha1-vwbs/ckARnEfTmF1VJJDt4zrOKo=", 80 | "dev": true 81 | }, 82 | "cscodegen": { 83 | "version": "git://github.com/michaelficarra/cscodegen.git#73fd7202ac086c26f18c9d56f025b18b3c6f5383", 84 | "from": "git://github.com/michaelficarra/cscodegen.git#73fd7202ac086c26f18c9d56f025b18b3c6f5383", 85 | "dev": true, 86 | "optional": true 87 | }, 88 | "escodegen": { 89 | "version": "0.0.28", 90 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-0.0.28.tgz", 91 | "integrity": "sha1-Dk/xcV8yh3XWyrUaxEpAbNer/9M=", 92 | "dev": true, 93 | "optional": true, 94 | "requires": { 95 | "esprima": "~1.0.2", 96 | "estraverse": "~1.3.0", 97 | "source-map": ">= 0.1.2" 98 | }, 99 | "dependencies": { 100 | "esprima": { 101 | "version": "1.0.4", 102 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", 103 | "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", 104 | "dev": true, 105 | "optional": true 106 | }, 107 | "estraverse": { 108 | "version": "1.3.2", 109 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.3.2.tgz", 110 | "integrity": "sha1-N8K4k+8T1yPydth41g2FNRUqbEI=", 111 | "dev": true, 112 | "optional": true 113 | } 114 | } 115 | }, 116 | "esmangle": { 117 | "version": "0.0.17", 118 | "resolved": "https://registry.npmjs.org/esmangle/-/esmangle-0.0.17.tgz", 119 | "integrity": "sha1-TFyTYHzeXRJ2utOW6DYinbpo2Qw=", 120 | "dev": true, 121 | "optional": true, 122 | "requires": { 123 | "escodegen": "~ 0.0.28", 124 | "escope": "~ 1.0.0", 125 | "esprima": "~ 1.0.2", 126 | "esshorten": "~ 0.0.2", 127 | "estraverse": "~ 1.3.2", 128 | "optimist": "*", 129 | "source-map": "~ 0.1.8" 130 | }, 131 | "dependencies": { 132 | "escope": { 133 | "version": "1.0.3", 134 | "resolved": "https://registry.npmjs.org/escope/-/escope-1.0.3.tgz", 135 | "integrity": "sha1-dZ3OhJbEJI/sLQyq9BCLzz8af10=", 136 | "dev": true, 137 | "optional": true, 138 | "requires": { 139 | "estraverse": "^2.0.0" 140 | }, 141 | "dependencies": { 142 | "estraverse": { 143 | "version": "2.0.0", 144 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-2.0.0.tgz", 145 | "integrity": "sha1-WuRpYyQ2ACBmdMyySgnhZnT83KE=", 146 | "dev": true, 147 | "optional": true 148 | } 149 | } 150 | }, 151 | "esprima": { 152 | "version": "1.0.4", 153 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", 154 | "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", 155 | "dev": true, 156 | "optional": true 157 | }, 158 | "esshorten": { 159 | "version": "0.0.2", 160 | "resolved": "https://registry.npmjs.org/esshorten/-/esshorten-0.0.2.tgz", 161 | "integrity": "sha1-KKZS8e/UDI4if4xt59vmtWDugSk=", 162 | "dev": true, 163 | "optional": true, 164 | "requires": { 165 | "escope": "~ 1.0.0", 166 | "estraverse": "~ 1.2.0" 167 | }, 168 | "dependencies": { 169 | "estraverse": { 170 | "version": "1.2.0", 171 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.2.0.tgz", 172 | "integrity": "sha1-aj3IpGpdZ2blZoY5/Hgpds5WYP0=", 173 | "dev": true, 174 | "optional": true 175 | } 176 | } 177 | }, 178 | "estraverse": { 179 | "version": "1.3.2", 180 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.3.2.tgz", 181 | "integrity": "sha1-N8K4k+8T1yPydth41g2FNRUqbEI=", 182 | "dev": true, 183 | "optional": true 184 | } 185 | } 186 | }, 187 | "nopt": { 188 | "version": "2.1.2", 189 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz", 190 | "integrity": "sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=", 191 | "dev": true, 192 | "requires": { 193 | "abbrev": "1" 194 | }, 195 | "dependencies": { 196 | "abbrev": { 197 | "version": "1.0.9", 198 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", 199 | "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", 200 | "dev": true 201 | } 202 | } 203 | }, 204 | "source-map": { 205 | "version": "0.1.11", 206 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.11.tgz", 207 | "integrity": "sha1-Lu8v1lp0wXmICuXuaXXZnOIet7Q=", 208 | "dev": true, 209 | "optional": true, 210 | "requires": { 211 | "amdefine": ">=0.0.4" 212 | }, 213 | "dependencies": { 214 | "amdefine": { 215 | "version": "1.0.0", 216 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz", 217 | "integrity": "sha1-/RdHRwDLXMnCtwnwvp0jzjwZjDM=", 218 | "dev": true, 219 | "optional": true 220 | } 221 | } 222 | } 223 | } 224 | } 225 | } 226 | }, 227 | "fs-plus": { 228 | "version": "2.9.1", 229 | "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-2.9.1.tgz", 230 | "integrity": "sha1-JbxJ70EkQa1m/fWXSp8PlC+nhEs=", 231 | "dev": true, 232 | "requires": { 233 | "async": "~0.2.9", 234 | "mkdirp": "~0.3.5", 235 | "rimraf": "~2.2.2", 236 | "underscore-plus": "1.x" 237 | }, 238 | "dependencies": { 239 | "async": { 240 | "version": "0.2.10", 241 | "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", 242 | "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", 243 | "dev": true 244 | }, 245 | "mkdirp": { 246 | "version": "0.3.5", 247 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", 248 | "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", 249 | "dev": true 250 | }, 251 | "rimraf": { 252 | "version": "2.2.8", 253 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", 254 | "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", 255 | "dev": true 256 | } 257 | } 258 | }, 259 | "optimist": { 260 | "version": "0.4.0", 261 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.4.0.tgz", 262 | "integrity": "sha1-y47Dfy/jqphky2eidSUOfhliCiU=", 263 | "dev": true, 264 | "requires": { 265 | "wordwrap": "~0.0.2" 266 | }, 267 | "dependencies": { 268 | "wordwrap": { 269 | "version": "0.0.3", 270 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 271 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", 272 | "dev": true 273 | } 274 | } 275 | }, 276 | "underscore-plus": { 277 | "version": "1.6.6", 278 | "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.6.6.tgz", 279 | "integrity": "sha1-ZezeG9xEGjXYnmUP1w3PE65Dmn0=", 280 | "dev": true, 281 | "requires": { 282 | "underscore": "~1.6.0" 283 | }, 284 | "dependencies": { 285 | "underscore": { 286 | "version": "1.6.0", 287 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", 288 | "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", 289 | "dev": true 290 | } 291 | } 292 | } 293 | } 294 | }, 295 | "underscore-plus": { 296 | "version": "1.6.6", 297 | "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.6.6.tgz", 298 | "integrity": "sha1-ZezeG9xEGjXYnmUP1w3PE65Dmn0=", 299 | "dev": true, 300 | "requires": { 301 | "underscore": "~1.6.0" 302 | }, 303 | "dependencies": { 304 | "underscore": { 305 | "version": "1.6.0", 306 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", 307 | "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", 308 | "dev": true 309 | } 310 | } 311 | } 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "language-haskell", 3 | "version": "1.24.0", 4 | "description": "Haskell language support in Atom", 5 | "repository": "https://github.com/atom-haskell/language-haskell", 6 | "license": "MIT", 7 | "engines": { 8 | "atom": ">=1.8.0 <2.0.0" 9 | }, 10 | "devDependencies": { 11 | "atom-grammar-test": "^0.6.4", 12 | "clone": "^1.0.2", 13 | "coffee-script": "~1.9.0", 14 | "season": "^2.0.0", 15 | "underscore-plus": "^1.6.6" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /settings/language-haskell.cson: -------------------------------------------------------------------------------- 1 | '.source.haskell': 2 | 'editor': 3 | 'commentStart': '-- ' 4 | 'increaseIndentPattern': '(((=|\\bdo|\\bwhere|\\bthen|\\belse|\\bof)\\s*)|(\\bif(?!.*\\bthen\\b.*\\belse\\b.*).*))$' 5 | autocomplete: 6 | symbols: 7 | function: 8 | selector: '.entity.name.function' 9 | typePriority: 1 10 | import: 11 | selector: '.support.other.module' 12 | typePriority: 1 13 | type: 14 | selector: '.entity.name.type' 15 | typePriority: 2 16 | tag: 17 | selector: '.entity.name.tag' 18 | typePriority: 1 19 | keyword: 20 | selector: '.keyword' 21 | typePriority: 9 22 | variable: 23 | selector: '.identifier' 24 | typePriority: 0 25 | '.source.cabal': 26 | 'editor': 27 | 'commentStart':'-- ' 28 | -------------------------------------------------------------------------------- /snippets/language-haskell.cson: -------------------------------------------------------------------------------- 1 | '.source .haskell': 2 | 'Pragma': 3 | 'prefix': '{-#' 4 | 'body': '{-# $1 #-' 5 | 'Module': 6 | 'prefix': 'module' 7 | 'body': 8 | """ 9 | module ${1:Name} 10 | ( 11 | ) where 12 | """ 13 | 'Definition': 14 | 'prefix': '=' 15 | 'body': '${1:name} ${2:pattern} = ${0:definition}' 16 | 'Function': 17 | 'prefix': 'fun' 18 | 'body': ''' 19 | ${1:name} :: ${2:Type} 20 | ${1} ${3:pattern} = ${4:definition} 21 | ''' 22 | '#!/usr/bin/env…': 23 | 'prefix': '#!' 24 | 'body': '#!/usr/bin/env ${1:runhaskell}' 25 | 'Lambda Expression': 26 | 'prefix': '\\' 27 | 'body': "\\\\${1:pattern} -> ${0:expression}" 28 | 'Left Arrow': 29 | 'prefix': '<' 30 | 'body': '${1:name} <- ${0:expression}' 31 | 'Main': 32 | 'prefix': 'main' 33 | 'body': ''' 34 | module Main where 35 | 36 | main :: IO () 37 | main = ${0:putStrLn "Hello World"} 38 | ''' 39 | 'Right Arrow': 40 | 'prefix': '>' 41 | 'body': '${1:expression} -> ${0:expression}' 42 | 'case … of …': 43 | 'prefix': 'case' 44 | 'body': ''' 45 | case ${1:expression} of 46 | \t${2:pattern} -> ${3:expression} 47 | \t${4:otherwise} -> ${5:expression} 48 | ''' 49 | 'class …': 50 | 'prefix': 'cla' 51 | 'body': ''' 52 | class ${1:Class} where 53 | \t${0:definition} 54 | ''' 55 | 'data …': 56 | 'prefix': 'dat' 57 | 'body': 'data ${1:Type} = ${0:Other}' 58 | 'do …': 59 | 'prefix': 'do' 60 | 'body': ''' 61 | do 62 | \t${1:return ${0:expression}} 63 | ''' 64 | 'if … then … else …': 65 | 'prefix': 'if' 66 | 'body': ''' 67 | if ${1:condition} 68 | \tthen ${2:expression} 69 | \telse ${3:expression} 70 | ''' 71 | 'import … hiding …': 72 | 'prefix': 'imph' 73 | 'body': 'import ${1:Module} hiding (${2:function})' 74 | 'import …': 75 | 'prefix': 'imp' 76 | 'body': 'import ${1:Module}' 77 | 'import qualified …': 78 | 'prefix': 'impq' 79 | 'body': 'import qualified ${1:Module}${2: as ${3:Mod}}' 80 | 'instance …': 81 | 'prefix': 'ins' 82 | 'body': ''' 83 | instance ${1:Class} ${2:Type} where 84 | \t${0:definition} 85 | ''' 86 | 'let …': 87 | 'prefix': 'let' 88 | 'body': ''' 89 | let 90 | \t${1:name} = ${2:expression} 91 | \tin ${0:expression} 92 | ''' 93 | 'newtype …': 94 | 'prefix': 'new' 95 | 'body': 'newtype ${1:Type} = ${0:Other}' 96 | 'type …': 97 | 'prefix': 'typ' 98 | 'body': 'type ${1:Type} = ${0:Other}' 99 | 'where …': 100 | 'prefix': 'where' 101 | 'body': ''' 102 | where 103 | \t${0:definitions} 104 | ''' 105 | 'INLINE': 106 | 'prefix': 'INL' 107 | 'body': '{-# INLINE ${1:function} #-}' 108 | 'NOINLINE': 109 | 'prefix': 'NOINL' 110 | 'body': '{-# NOINLINE ${1:function} #-}' 111 | 'INLINABLE': 112 | 'prefix': 'INLINABLE' 113 | 'body': '{-# INLINABLE ${1:function} #-}' 114 | 'LANGUAGE': 115 | 'prefix': 'LANG' 116 | 'body': '{-# LANGUAGE ${1:ext} #-}' 117 | 'UNPACK': 118 | 'prefix': 'UNPACK' 119 | 'body': '{-# UNPACK #-} ' 120 | 'OPTIONS_GHC': 121 | 'prefix': 'GHC' 122 | 'body': '{-# OPTIONS_GHC -${1:option} #-}' 123 | ###### UNICODE ###### 124 | 'Proportion': 125 | 'prefix': '::' 126 | 'body': '∷' #0x2237 127 | 'Rightwards double arrow': 128 | 'prefix': '=>' 129 | 'body': '⇒' 130 | 'For all': 131 | 'prefix': 'forall' 132 | 'body': '∀' #0x2200 133 | 'Rightwards arrow': 134 | 'prefix': '->' 135 | 'body': '→' # 0x2192 136 | 'Leftwards arrow': 137 | 'prefix': '<-' 138 | 'body': '←' #0x2190 139 | 'Leftwards arrow-tail': 140 | 'prefix': '-<' 141 | 'body': '↢' #0x2919 142 | 'Rightwards arrow-tail': 143 | 'prefix': '>-' 144 | 'body': '↣' #0x291A 145 | 'Leftwards double arrow-tail': 146 | 'prefix': '-<<' 147 | 'body': '⤛' # 0x291B 148 | 'Rightwards double arrow-tail': 149 | 'prefix': '>>-' 150 | 'body': '⤜' # 0x291C 151 | 'Black star': 152 | 'prefix': '*' 153 | 'body': '★' #0x2605 154 | '.source .haskell:not(.comment)': 155 | 'Guard': 156 | 'prefix': '|' 157 | 'body': '| ${1:predicate} = ${0:definition}' 158 | '.source .haskell.comment': 159 | 'Haddock Postfix': 160 | 'prefix': '|' 161 | 'body': '| ${0:documentation}' 162 | 'Haddock Prefix': 163 | 'prefix': '^' 164 | 'body': '^ ${0:documentation}' 165 | '.source .haskell.constant.language.empty-list': 166 | 'List Comprehension': 167 | 'prefix': '[' 168 | 'body': '[ ${1:expression} | ${2:${3:name} <- ${4:expression}}$0' 169 | '.source .haskell.meta.type': 170 | 'deriving …': 171 | 'prefix': 'der' 172 | 'body': 'deriving (${0:Class})' 173 | '.source.cabal': 174 | 'executable': 175 | 'prefix': 'exe' 176 | 'body': ''' 177 | executable ${1:exename} 178 | hs-source-dirs: ${2:app} 179 | main-is: ${3:Main.hs} 180 | ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N 181 | build-depends: base 182 | , ${4:libname} 183 | default-language: Haskell2010 184 | ''' 185 | 'library': 186 | 'prefix': 'lib' 187 | 'body': ''' 188 | library 189 | hs-source-dirs: ${1:src} 190 | exposed-modules: ${2:Lib} 191 | other-modules: ${3:Module} 192 | build-depends: base >= 4 && < 5 193 | ghc-options: -Wall 194 | default-language: Haskell2010 195 | ''' 196 | 'repository': 197 | 'prefix': 'repo' 198 | 'body': ''' 199 | source-repository head 200 | type: git 201 | location: https://github.com/${1:githubuser}/${2:project} 202 | ''' 203 | 'test': 204 | 'prefix': 'test' 205 | 'body': ''' 206 | test-suite ${1:test-name} 207 | type: exitcode-stdio-1.0 208 | hs-source-dirs: ${2:test} 209 | main-is: ${3:Spec.hs} 210 | build-depends: base 211 | , ${4:libname} 212 | ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N 213 | default-language: Haskell2010 214 | ''' 215 | -------------------------------------------------------------------------------- /spec/fixture/charKind.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | -- see https://gitlab.haskell.org/ghc/ghc/-/blob/9c9e40e59214b1e358c85852218f3a67e712a748/testsuite/tests/typecheck/T11342/T11342a.hs 3 | -- https://github.com/atom-haskell/language-haskell/issues/128 4 | {-# LANGUAGE DataKinds #-} 5 | {-# LANGUAGE KindSignatures #-} 6 | {-# LANGUAGE TypeOperators #-} 7 | 8 | module T11342a where 9 | 10 | import Data.Type.Equality 11 | 12 | type A = '"' :: Char 13 | -- ^^^^ meta.declaration.type.type meta.type-signature entity.name.type support.class.prelude.Char 14 | -- ^^^ meta.declaration.type.type meta.type-signature string.quoted.single 15 | 16 | -- I am a blue comment 17 | 18 | t :: '"' :~: '"' 19 | -- ^^^ meta.function.type-declaration meta.type-signature string.quoted.single 20 | -- ^^^ meta.function.type-declaration meta.type-signature string.quoted.single 21 | -- ^^^ meta.function.type-declaration meta.type-signature keyword.operator 22 | t = Refl 23 | -- ^^^^ entity.name.tag 24 | -------------------------------------------------------------------------------- /spec/fixture/deriveStrategy.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | 3 | data New = Somtehing deriving stock Something 4 | -- ^^^^^^^^^ meta.deriving entity.other.inherited-class 5 | -- ^^^^^ meta.deriving meta.deriving.strategy keyword.other 6 | -- ^^^^^^^^ meta.declaration.type.data meta.deriving keyword.other 7 | 8 | data New = Somtehing deriving newtype Something 9 | -- ^^^^^^^ meta.deriving meta.deriving.strategy keyword.other 10 | 11 | data New = Somtehing deriving anyclass Something 12 | -- ^^^^^^^^ meta.deriving meta.deriving.strategy keyword.other 13 | 14 | data New = Somtehing deriving stock (Something, Other) 15 | -- ^^^^^^^^^ meta.deriving entity.other.inherited-class 16 | -- ^^^^^ meta.deriving meta.deriving.strategy keyword.other 17 | -- ^^^^^^^^ meta.declaration.type.data meta.deriving keyword.other 18 | 19 | data New = Somtehing deriving newtype (Something, Other) 20 | -- ^^^^^^^ meta.deriving meta.deriving.strategy keyword.other 21 | 22 | data New = Somtehing deriving anyclass (Something, Other) 23 | -- ^^^^^^^^ meta.deriving meta.deriving.strategy keyword.other 24 | 25 | data New = Somtehing deriving stock 26 | -- ^^^^^ meta.deriving meta.deriving.strategy keyword.other 27 | -- ^^^^^^^^ meta.declaration.type.data meta.deriving keyword.other 28 | 29 | data New = Somtehing deriving newtype 30 | -- ^^^^^^^ meta.deriving meta.deriving.strategy keyword.other 31 | 32 | data New = Somtehing deriving anyclass 33 | -- ^^^^^^^^ meta.deriving meta.deriving.strategy keyword.other 34 | 35 | -- standalone deriving 36 | 37 | deriving instance Something New 38 | -- ^^^ meta.type-signature entity.name.type 39 | -- ^^^^^^^^^ meta.type-signature entity.name.type 40 | -- ^^^^^^^^ keyword.other 41 | -- <- meta.declaration.instance.deriving keyword.other 42 | 43 | deriving stock instance Something New 44 | -- ^^^^^ meta.deriving.strategy keyword.other 45 | -- ^^^ meta.type-signature entity.name.type 46 | -- ^^^^^^^^^ meta.type-signature entity.name.type 47 | -- ^^^^^^^^ keyword.other 48 | -- <- meta.declaration.instance.deriving keyword.other 49 | 50 | deriving anyclass instance Something New 51 | -- ^^^^^^^^ meta.deriving.strategy keyword.other 52 | 53 | deriving newtype instance Something New 54 | -- ^^^^^^^ meta.deriving.strategy keyword.other 55 | 56 | -- deriving via 57 | 58 | newtype New = New Int deriving Generic via Int 59 | -- ^^^ meta.via entity.name.type 60 | -- ^^^ meta.declaration.type.data meta.via keyword.other 61 | 62 | newtype New = New Int deriving Generic via (Hex Int) 63 | -- ^^^ keyword.other 64 | -- ^^^ entity.name.type 65 | -- ^^^ entity.name.type 66 | -- ^^^^^^^^^^^^^ meta.declaration.type.data meta.via 67 | deriving via Lift (ReaderT r) m instance (MonadExit m) => (MonadExit (ReaderT r m)) 68 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.instance.deriving meta.type-signature 69 | -- ^^^^^^^^^^^^^^^^^^ meta.declaration.instance.deriving meta.type-signature 70 | -- ^^^ ^^^^^^^^ meta.declaration.instance.deriving keyword.other 71 | 72 | data Foo = Bar deriving Class via (Baz Quux FooBar) 73 | -- ^^^ ^^^^ ^^^^^^ meta.declaration.type.data meta.via entity.name.type 74 | data Foo = Bar deriving Class via Baz Quux FooBar 75 | -- ^^^ ^^^^ ^^^^^^ meta.declaration.type.data meta.via entity.name.type 76 | data Foo = Bar deriving Class 77 | via Baz 78 | Quux FooBar 79 | -- ^^^^ ^^^^^^ meta.declaration.type.data meta.via entity.name.type 80 | data Foo = Bar deriving Class 81 | via Baz 82 | Quux FooBar 83 | -- ^^^^ ^^^^^^ meta.declaration.type.data meta.via entity.name.type 84 | -------------------------------------------------------------------------------- /spec/fixture/gadt.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | 3 | {-# LANGUAGE GADTs #-} 4 | 5 | module Test where 6 | 7 | -- it understands GADT syntax 8 | 9 | data Term a where 10 | -- ^^^^^ meta.declaration.type.data keyword.other 11 | -- ^ meta.declaration.type.data meta.type-signature variable.other.generic-type 12 | -- ^^^^ meta.declaration.type.data meta.type-signature entity.name.type 13 | -- <- meta.declaration.type.data keyword.other.data 14 | LitI :: Int -> Term Int 15 | -- ^^^ meta.type-signature entity.name.type support.class.prelude.Int 16 | -- ^^^^ meta.type-signature entity.name.type 17 | -- ^^ meta.type-signature keyword.other.arrow 18 | -- ^^^ meta.type-signature entity.name.type support.class.prelude.Int 19 | -- ^^^^ entity.name.tag 20 | LitS :: String -> Term String 21 | -- ^^^^^^ meta.type-signature entity.name.type support.class.prelude.String 22 | -- ^^^^ meta.type-signature entity.name.type 23 | -- ^^ meta.type-signature keyword.other.arrow 24 | -- ^^^^^^ meta.type-signature entity.name.type support.class.prelude.String 25 | -- ^^^^ entity.name.tag 26 | 27 | f :: a 28 | f = undefined 29 | -- >> =source.haskell 30 | -------------------------------------------------------------------------------- /spec/fixture/general.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | 3 | {-# LANGUAGE DataKinds, KindSignatures, CPP #-} 4 | 5 | module Test ( 6 | main, 7 | -- ^^^^ meta.declaration.module meta.declaration.exports entity.name.function 8 | #ifdef TEST 9 | -- ^^^^ meta.preprocessor.c 10 | -- <- meta.preprocessor.c 11 | module Test 12 | -- ^^^^ meta.declaration.module meta.declaration.exports support.other.module 13 | -- ^^^^^^ meta.declaration.module meta.declaration.exports keyword.other 14 | #else 15 | -- <- meta.preprocessor.c 16 | str1, Test.str2, 17 | -- ^^^^ meta.declaration.module meta.declaration.exports entity.name.function 18 | -- ^^^^^ support.other.module 19 | -- ^^^^ meta.declaration.module meta.declaration.exports entity.name.function 20 | DataType( 21 | -- ^^^^^^^^ meta.declaration.module meta.declaration.exports entity.name.type 22 | #ifdef EXPORT_CTOR 23 | -- <- meta.preprocessor.c 24 | TypeCtor 25 | -- ^^^^^^^^ meta.declaration.module meta.declaration.exports meta.other.constructor-list entity.name.tag 26 | #else 27 | .. 28 | -- ^^ meta.declaration.module meta.declaration.exports meta.other.constructor-list keyword.operator.wildcard 29 | #endif 30 | -- <- meta.preprocessor.c 31 | ), 32 | Test.DataType 33 | -- ^^^^^^^^^^^^^ entity.name.type 34 | -- ^^^^^ support.other.module 35 | #endif 36 | -- <- meta.preprocessor.c 37 | ) where 38 | 39 | import GHC.Types 40 | 41 | data DataType = TypeCtor Int 42 | -- ^^^ meta.declaration.type.data meta.type-signature entity.name.type support.class.prelude.Int 43 | -- ^^^^^^^^ meta.declaration.type.data entity.name.tag 44 | -- ^^^^^^^^ meta.declaration.type.data meta.type-signature entity.name.type 45 | -- <- meta.declaration.type.data keyword.other.data 46 | 47 | data Obj = forall a. (Show a) => Obj a 48 | -- ^^^ entity.name.tag 49 | -- ^^ keyword.other.big-arrow 50 | -- ^^^^ entity.other.inherited-class.prelude.Show 51 | -- ^^^^^^ keyword.other.forall 52 | 53 | data Obj = forall a. Show a => Obj a 54 | -- ^^^ entity.name.tag 55 | -- ^^ keyword.other.big-arrow 56 | -- ^^^^ entity.other.inherited-class.prelude.Show 57 | -- ^^^^^^ keyword.other.forall 58 | 59 | data Obj = forall a 60 | -- ^^^^^^ keyword.other.forall 61 | . (Show a) => Obj a 62 | -- ^^^^ entity.other.inherited-class.prelude.Show 63 | -- ^^^ entity.name.tag 64 | -- ^^ keyword.other.big-arrow 65 | 66 | data Obj = forall a. (Show a) 67 | -- ^^^^^^ keyword.other.forall 68 | -- ^^^^ entity.other.inherited-class.prelude.Show 69 | => Obj a 70 | -- ^ variable.other.generic-type 71 | -- ^^^ entity.name.tag 72 | -- ^^ keyword.other.big-arrow 73 | 74 | data Obj = forall a. Obj a 75 | -- ^^^ entity.name.tag 76 | -- ^^^^^^ keyword.other.forall 77 | 78 | data Obj = forall a 79 | -- ^^^^^^ keyword.other.forall 80 | . Obj a 81 | -- ^^^ entity.name.tag 82 | 83 | ---------------------------------- Strings ------------------------------------- 84 | 85 | str1, str2 :: String 86 | 87 | -- it tokenizes single-line strings 88 | str1 = "abcde\n\EOT\\EOL" 89 | 90 | -- Regression test for #96 91 | str2 = "^\\ " 92 | -- ^^^^^^ string.quoted.double 93 | -- ^ punctuation.definition.string.end 94 | -- ^^ constant.character.escape 95 | -- ^ string.quoted.double punctuation.definition.string.begin 96 | 97 | -- it supports type-level string literals 98 | str3 :: Fake "type-level string" 99 | -- ^^^^^^^^^^^^^^^^^^^ meta.function.type-declaration meta.type-signature string.quoted.double 100 | str3 = undefined 101 | type Fake (a :: Symbol) = String 102 | 103 | --------------------------- infix function call -------------------------------- 104 | infixCall :: t 105 | infixCall = a `func` b 106 | -- ^^^^^^ keyword.operator.function.infix 107 | where (a, b, func) = undefined 108 | 109 | --------------------------- data declarations --------------------------- 110 | 111 | data Foo = Foo Int 112 | -- ^^^ meta.declaration.type.data meta.type-signature entity.name.type 113 | -- ^^^ meta.declaration.type.data entity.name.tag 114 | -- ^^^ meta.declaration.type.data meta.type-signature entity.name.type 115 | 116 | main :: IO () 117 | main = undefined 118 | 119 | -- no unnecessary termination in data declarations 120 | data Something ~= Something 121 | -- ^^^^^^^^^ meta.declaration.type.data meta.type-signature entity.name.type 122 | data Something ~--= Something 123 | -- ^^^^^^^^^ meta.declaration.type.data meta.type-signature entity.name.type 124 | -- proper termination in data declarations 125 | data Something --- Something = Something 126 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^ comment.line.double-dash 127 | data Something = Something 128 | -- ^ keyword.operator.assignment 129 | -- ^^^^^^^^^ entity.name.tag 130 | -- ^^^^^^^^^ entity.name.type 131 | data Something -- = Something 132 | -- ^^^^^^^^^^^^^^ comment.line.double-dash 133 | -- ^^^^^^^^^ entity.name.type 134 | data Something a {- =b -} aa = Other 135 | -- ^^^^^^^^ comment.block 136 | -- ^^ meta.type-signature 137 | -- ^^^^^^^^^ meta.type-signature entity.name.type 138 | data Something {- = Something -} = Other 139 | -- ^^^^^^^^^^^^^^^^^ comment.block 140 | 141 | func2 :: IO () {- comment = also comment -} 142 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comment.block 143 | func2 :: IO () !{- comment = also comment -} 144 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comment.block 145 | func :: IO () -- comment = also comment 146 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^ comment.line.double-dash 147 | func :: IO () !-- comment = also comment 148 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^ !comment.line.double-dash 149 | let x :: Int -- comment <- an int 150 | -- ^^^^^^^^^^^^^^^^^^^^ comment.line.double-dash 151 | let x :: Int {- comment <- an int -} 152 | -- ^^^^^^^^^^^^^^^^^^^^^^^ comment.block 153 | let x :: Int !-- comment <- an int 154 | -- ^^^^^^^^^^^^^^^^^^^^ !comment.line.double-dash 155 | let x :: Int !{- comment <- an int -} 156 | -- ^^^^^^^^^^^^^^^^^^^^^^^ comment.block 157 | 158 | -- multi-line type constructor definitions 159 | data PhExpr id 160 | = PhVar id 161 | | OpApp (LPhExpr id) 162 | -- ^^^^^^^ meta.declaration.type.data meta.type-signature entity.name.type 163 | (LPhExpr id) 164 | -- ^^^^^^^ meta.declaration.type.data meta.type-signature entity.name.type 165 | (LPhExpr id) 166 | -- ^^^^^^^ meta.declaration.type.data meta.type-signature entity.name.type 167 | | NegApp (LPhExpr id) 168 | -- ^^^^^^^ meta.declaration.type.data meta.type-signature entity.name.type 169 | 170 | -- end 171 | 172 | x = [something|some text here|] 173 | -- ^^^^^^^^^^^^^^ quoted.quasiquotes.qq-something 174 | 175 | -- >> =source.haskell 176 | -------------------------------------------------------------------------------- /spec/fixture/identifiers.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | module Test where 3 | 4 | main :: IO () 5 | -- <- meta.function.type-declaration entity.name.function 6 | main = do 7 | -- <- identifier 8 | 9 | -- it tokenizes identifiers 10 | aSimpleIdentifier 11 | -- ^^^^^^^^^^^^^^^^^ identifier 12 | 13 | -- it tokenizes identifiers with module names 14 | Some.Module.identifier 15 | -- ^^^^^^^^^^^^^^^^^^^^^^ identifier 16 | -- ^^^^^^^^^^^^ support.other.module 17 | 18 | -- it tokenizes type constructors 19 | SomeCtor 20 | -- ^^^^^^^^ entity.name.tag 21 | 22 | -- it tokenizes type constructors with module names 23 | Some.Module.SomeCtor 24 | -- ^^^^^^^^^^^^^^^^^^^^ entity.name.tag 25 | -- ^^^^^^^^^^^^ support.other.module 26 | 27 | -- it tokenizes identifiers with numeric parts 28 | numer123ident 29 | -- ^^^^^^^^^^^^^ identifier 30 | numerident123 31 | -- ^^^^^^^^^^^^^ identifier 32 | 123numerident 33 | -- ^^^ constant.numeric.decimal 34 | -- ^^^^^^^^^^ identifier 35 | 36 | -- it doesnt confuse identifiers starting with type (issue 84) 37 | typeIdentifier 38 | -- ^^^^^^^^^^^^^^ identifier 39 | 40 | -- it doesnt confuse identifiers starting with data 41 | dataIdentifier 42 | -- ^^^^^^^^^^^^^^ identifier 43 | 44 | -- it doesnt confuse identifiers starting with newtype 45 | newtypeIdentifier 46 | -- ^^^^^^^^^^^^^^^^^ identifier 47 | 48 | -- it parses identifiers with ' 49 | anIdentifierWith' 50 | -- ^^^^^^^^^^^^^^^^^ identifier 51 | anIdenti'fierWit'h 52 | -- ^^^^^^^^^^^^^^^^^^ identifier 53 | 54 | -- it doesn't confuse identifiers with and without ' 55 | foldl 56 | -- ^^^^^ identifier support.function.prelude.foldl 57 | foldl' 58 | -- ^^^^^^ identifier 59 | 60 | 61 | -- >> =source.haskell 62 | -------------------------------------------------------------------------------- /spec/fixture/liquidhaskell.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | module Intro where 3 | -- <- keyword.other 4 | -- ^^^^^ support.other.module 5 | -- ^^^^^ keyword.other 6 | -- ^^^^^^^^^^^^^^^ meta.declaration.module 7 | 8 | import Language.Haskell.Liquid.Prelude (liquidAssert) 9 | -- ^^^^^^^^^^^^ meta.declaration.exports entity.name.function 10 | -- <- keyword.other 11 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ support.other.module 12 | 13 | zero' :: Int 14 | zero' = 0 15 | 16 | {-@ zero' :: {v: Int | 0 <= v} @-} 17 | -- ^^^^^^^^^^^^^^^^^ liquid.type 18 | -- ^^ keyword.operator 19 | -- ^ constant.numeric 20 | -- ^^^ entity.name.type 21 | -- ^^^^^ entity.name.function 22 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^ block.liquidhaskell.annotation 23 | -- <- block.liquidhaskell 24 | 25 | {-@ zero'' :: {v: Int | (0 <= v && v < 100) } @-} 26 | -- ^^^^^^ entity.name.function 27 | -- ^^^ entity.name.type 28 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ liquid.type 29 | -- ^^ keyword.operator 30 | -- ^ keyword.operator 31 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ block.liquidhaskell.annotation 32 | -- <- block.liquidhaskell 33 | zero'' :: Int 34 | -- <- meta.function.type-declaration 35 | zero'' = 0 36 | -- <- identifier 37 | 38 | {-@ zero''' :: {v: Int | ((v mod 2) = 0) } @-} 39 | zero''' :: Int 40 | -- <- meta.function.type-declaration 41 | zero''' = 0 42 | -- <- identifier 43 | 44 | {-@ zero'''' :: {v: Int | v = 0 } @-} 45 | zero'''' :: Int 46 | zero'''' = 0 47 | 48 | {-@ zero :: {v: Int | ((0 <= v) && ((v mod 2) = 0) && (v < 100)) } @-} 49 | zero :: Int 50 | zero = 0 51 | 52 | {-@ error' :: {v: String | false } -> a @-} 53 | error' :: String -> a 54 | error' = error 55 | 56 | {-@ lAssert :: {v:Bool | (Prop v)} -> a -> a @-} 57 | lAssert :: Bool -> a -> a 58 | lAssert True x = x 59 | lAssert False _ = error' "lAssert failure" 60 | 61 | divide :: Int -> Int -> Int 62 | divide n 0 = error' "divide by zero" 63 | divide n d = n `div` d 64 | 65 | {-@ divide :: Int -> {v: Int | v != 0 } -> Int @-} 66 | 67 | {-@ divide' :: Int -> {v:Int | v /= 0} -> Int @-} 68 | divide' :: Int -> Int -> Int 69 | divide' n 0 = error' "divide by zero" 70 | divide' n d = lAssert (d /= 0) $ n `div` d 71 | 72 | abz :: Int -> Int 73 | abz n | 0 < n = n 74 | | otherwise = 0 - n 75 | 76 | {-@ abz :: Int -> {v: Int | 0 <= v } @-} 77 | -- ^^^^^^^^^^^^^^^^^^ liquid.type 78 | -- ^^^ entity.name.type 79 | -- ^^^ entity.name.function 80 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ block.liquidhaskell.annotation 81 | -- <- block.liquidhaskell 82 | 83 | {-@ truncate :: Int -> Int -> Int @-} 84 | truncate i max 85 | | i' <= max' = i 86 | | otherwise = max' * (i `divide` i') 87 | where i' = abz i 88 | max' = abz max 89 | 90 | {-@ truncate' :: Int -> Int -> Int @-} 91 | truncate' i max 92 | | i' <= max' = i 93 | | otherwise = lAssert (i' /= 0) $ max' * (i `divide` i') 94 | where i' = abz i 95 | max' = abz max 96 | 97 | {-@ truncate'' :: Int -> Int -> Int @-} 98 | truncate'' i max 99 | | i' <= max' = i 100 | | otherwise = liquidAssert (i' /= 0) $ max' * (i `divide` i') 101 | where i' = abz i 102 | max' = abz max 103 | 104 | {-@ listAssoc :: x:List a -> y:List a -> z:List a 105 | -> {(append x (append y z)) == (append (append x y) z) } @-} 106 | -- ^^^^^^ ^ ^^^^^^ ^ ^ ^^^^^^ ^^^^^^ ^ ^ ^ identifier 107 | {-@ type Something = SomethingElse @-} 108 | -- <- meta.declaration.type 109 | {-@ instance Something where 110 | -- <- meta.declaration.instance 111 | asd = instance 112 | -- <- identifier 113 | @-} 114 | -- >> =source.haskell 115 | -------------------------------------------------------------------------------- /spec/fixture/module-exports.hs: -------------------------------------------------------------------------------- 1 | -- Regression test for #118 2 | 3 | module MyList 4 | ( MyList(Nil,(:>)) 5 | -- ^^^^ meta.declaration.module meta.declaration.exports meta.other.constructor-list entity.name.function.operator 6 | -- ^^^ meta.declaration.module meta.declaration.exports meta.other.constructor-list entity.name.tag 7 | -- ^^^^^^ meta.declaration.module meta.declaration.exports entity.name.type 8 | , myHead 9 | -- ^^^^^^ meta.declaration.module meta.declaration.exports entity.name.function 10 | ) where 11 | -------------------------------------------------------------------------------- /spec/fixture/multilineHaddock.hs: -------------------------------------------------------------------------------- 1 | -- | This is a 2 | -- ^^^^^^^^^^ comment.line.double-dash.haddock 3 | -- multiline haddock 4 | -- <- comment.line.double-dash.haddock 5 | -- definition 6 | -- ^^^^^^^^^^ comment.line.double-dash.haddock 7 | 8 | -- This is a regular comment 9 | -- <- comment.line.double-dash 10 | -- ^^^^^^^^^^^^^^^^^^^^^^^ comment.line.double-dash 11 | -------------------------------------------------------------------------------- /spec/fixture/multilineSignatures.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | 3 | {-# LANGUAGE TypeOperators, RankNTypes, ScopedTypeVariables, KindSignatures, TypeFamilies, GADTs #-} 4 | {-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-binds -fno-warn-unused-matches #-} 5 | 6 | module Test where 7 | -- ^^^^^ meta.declaration.module keyword.other 8 | -- ^^^^ meta.declaration.module support.other.module 9 | -- <- meta.declaration.module keyword.other 10 | 11 | someFunc :: String 12 | -- ^^^^^^ meta.function.type-declaration meta.type-signature entity.name.type support.class.prelude.String 13 | -> String 14 | -- ^^^^^^ meta.function.type-declaration meta.type-signature entity.name.type support.class.prelude.String 15 | 16 | someFunc'' 17 | :: String 18 | -- ^^^^^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.String 19 | -> String 20 | -- ^^^^^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.String 21 | 22 | someFunc' 23 | :: String 24 | -- ^^^^^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.String 25 | -> String 26 | -- ^^^^^^ entity.name.tag 27 | 28 | someFunc = undefined 29 | someFunc' = undefined 30 | someFunc'' = undefined 31 | 32 | f x = do 33 | stuff :: String <- x 34 | -- ^ identifier 35 | -- ^^^^^^ entity.name.type support.class.prelude.String 36 | -- ^^^^^^^^ meta.type-signature 37 | return () 38 | 39 | g x = do 40 | stuff 41 | :: String 42 | -- ^^^^^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.String 43 | -> String <- x 44 | -- ^^^^^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.String 45 | -- ^ identifier 46 | return () 47 | -- <- identifier support.function.prelude.return 48 | 49 | h x = do 50 | stuff 51 | :: String 52 | -- ^^^^^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.String 53 | ->> String 54 | -- ^^^^^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.String 55 | -- <- meta.multiline.type-declaration meta.type-signature keyword.operator 56 | --> IO String <- x 57 | -- ^^^^^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.String 58 | -- ^^ meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.IO 59 | -- ^^^ meta.multiline.type-declaration meta.type-signature keyword.operator 60 | -- ^ identifier 61 | return () 62 | data (->>) a b = DArr a b 63 | data (-->) a b = DDArr a b 64 | 65 | acquire 66 | :: forall res r (s :: * -> *) 67 | -- <- meta.multiline.type-declaration meta.type-signature keyword.other.arrow 68 | -- <- meta.multiline.type-declaration meta.type-signature keyword.operator support.operator.prelude 69 | -- <- meta.multiline.type-declaration meta.type-signature variable.other.generic-type 70 | -- <- meta.multiline.type-declaration meta.type-signature keyword.other.forall 71 | . ( s ~ Maybe 72 | -- <- meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.Maybe 73 | -- <- meta.multiline.type-declaration meta.type-signature keyword.operator 74 | -- <- meta.multiline.type-declaration meta.type-signature variable.other.generic-type 75 | -- <- meta.multiline.type-declaration meta.type-signature keyword.operator support.operator.prelude 76 | , Monad s 77 | -- <- meta.multiline.type-declaration meta.type-signature variable.other.generic-type 78 | -- <- meta.multiline.type-declaration meta.type-signature entity.name.type entity.other.inherited-class.prelude.Monad 79 | ) 80 | => Either res r 81 | -- <- meta.multiline.type-declaration meta.type-signature variable.other.generic-type 82 | -- <- meta.multiline.type-declaration meta.type-signature variable.other.generic-type 83 | -- <- meta.multiline.type-declaration meta.type-signature keyword.other.big-arrow 84 | -- <- meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.Either 85 | -> Either [res] [r] 86 | -- <- meta.multiline.type-declaration meta.type-signature variable.other.generic-type 87 | -- <- meta.multiline.type-declaration meta.type-signature variable.other.generic-type 88 | -- <- meta.multiline.type-declaration meta.type-signature entity.name.type support.class.prelude.Either 89 | -- <- meta.multiline.type-declaration meta.type-signature keyword.other.arrow 90 | acquire = undefined 91 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 92 | -- <- identifier 93 | 94 | function :: String -> Maybe Int = Just . read 95 | -- ^ entity.name.tag 96 | -- <- identifier 97 | 98 | function' :: String -> Maybe Int 99 | -- <- meta.function.type-declaration meta.type-signature entity.name.type support.class.prelude.String 100 | -- <- meta.function.type-declaration entity.name.function 101 | = Just . read 102 | -- ^ entity.name.tag 103 | 104 | smth = do 105 | x :: Int 106 | -- <- meta.function.type-declaration meta.type-signature entity.name.type support.class.prelude.Int 107 | <- Just 1 108 | -- ^ constant.numeric.decimal 109 | -- ^ entity.name.tag 110 | return () 111 | -- >> =source.haskell 112 | -------------------------------------------------------------------------------- /spec/fixture/patternsTest.hs: -------------------------------------------------------------------------------- 1 | module SomeModule 2 | ( pattern SomePat 3 | -- ^^^^^^^ meta.declaration.module meta.declaration.exports meta.declaration.export.qualified.pattern keyword.other.pattern 4 | -- ^^^^^^^ meta.declaration.module meta.declaration.exports meta.declaration.export.qualified.pattern entity.name.tag 5 | , type OtherPat 6 | -- ^^^^ meta.declaration.module meta.declaration.exports meta.declaration.export.qualified.type keyword.other.type 7 | -- ^^^^^^^^ meta.declaration.module meta.declaration.exports meta.declaration.export.qualified.type entity.name.type 8 | , OtherPat 9 | -- ^^^^^^^^ meta.declaration.module meta.declaration.exports entity.name.type 10 | , Eq(..) 11 | -- ^^ meta.declaration.module meta.declaration.exports meta.other.constructor-list keyword.operator.wildcard 12 | , type (++) 13 | -- ^^^^ meta.declaration.module meta.declaration.exports meta.declaration.export.qualified.type keyword.other.type 14 | -- ^^ meta.declaration.module meta.declaration.exports meta.declaration.export.qualified.type keyword.operator support.operator.prelude 15 | , pattern (:|) 16 | -- ^^^^^^^ meta.declaration.module meta.declaration.exports meta.declaration.export.qualified.pattern keyword.other.pattern 17 | -- ^^^^ meta.declaration.module meta.declaration.exports meta.declaration.export.qualified.pattern entity.name.function.operator 18 | ) where 19 | 20 | fun :: (++) 21 | -- ^^ meta.function.type-declaration meta.type-signature keyword.operator support.operator.prelude 22 | 23 | pattern SomePat :: a -> a 24 | pattern SomePat x <- x 25 | -------------------------------------------------------------------------------- /spec/fixture/pragmasTest.hs: -------------------------------------------------------------------------------- 1 | module TestModule {-# DEPRECATED "Do not use this "#-} 2 | -- ^^^^^^^^^^ meta.declaration.module meta.preprocessor keyword.other.preprocessor.DEPRECATED 3 | ( export 4 | ) where 5 | 6 | {-# COMPLETE pattern #-} 7 | -- ^^^^^^^^ meta.preprocessor keyword.other.preprocessor.COMPLETE 8 | -------------------------------------------------------------------------------- /spec/fixture/record.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | module Test where 3 | 4 | -- it understands record syntax 5 | data Car1 = Car1 { 6 | -- ^ meta.declaration.type.data meta.declaration.type.data.record.block keyword.operator.record.begin 7 | -- ^^^^ meta.declaration.type.data meta.type-signature entity.name.type 8 | -- ^^^^ meta.declaration.type.data entity.name.tag 9 | company :: String, 10 | -- ^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration keyword.other.double-colon 11 | -- ^^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration entity.other.attribute-name 12 | -- ^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature entity.name.type support.class.prelude.String 13 | model :: String, 14 | -- ^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration keyword.other.double-colon 15 | -- ^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature entity.name.type support.class.prelude.String 16 | -- ^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration entity.other.attribute-name 17 | year :: Int 18 | -- ^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration keyword.other.double-colon 19 | -- ^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration entity.other.attribute-name 20 | -- ^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature entity.name.type support.class.prelude.Int 21 | } deriving (Show) 22 | -- ^^^^ meta.declaration.type.data meta.deriving entity.other.inherited-class 23 | -- ^^^^^^^^ meta.declaration.type.data meta.deriving keyword.other 24 | -- ^ meta.declaration.type.data meta.declaration.type.data.record.block keyword.operator.record.end 25 | 26 | -- it understands comments in records 27 | data Car2 = Car2 { 28 | company2 :: String, -- comment 29 | -- ^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature entity.name.type support.class.prelude.String 30 | -- ^^^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration entity.other.attribute-name 31 | -- model :: String, -- commented field 32 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature comment.line.double-dash 33 | year2 :: Int -- another comment 34 | -- ^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature entity.name.type support.class.prelude.Int 35 | -- ^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration entity.other.attribute-name 36 | } 37 | 38 | -- it understands comments in start of records 39 | newtype Car3 = Car3 { 40 | -- company :: String 41 | -- ^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block comment.line.double-dash 42 | model3 :: String 43 | -- ^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature entity.name.type support.class.prelude.String 44 | -- ^^^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration entity.other.attribute-name 45 | } 46 | 47 | --------------- regression test for #65 --------------- 48 | 49 | -- it works without space 50 | 51 | data Foo = Foo{bar :: Int} 52 | -- ^ meta.declaration.type.data meta.declaration.type.data.record.block keyword.operator.record.begin 53 | -- ^ meta.declaration.type.data meta.declaration.type.data.record.block keyword.operator.record.end 54 | -- ^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature entity.name.type 55 | -- ^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration entity.other.attribute-name 56 | -- ^^^ meta.declaration.type.data entity.name.tag 57 | -- ^^^ meta.declaration.type.data meta.type-signature entity.name.type 58 | 59 | -- it works with space 60 | data Foo1 = Foo1 {bar1 :: Int} 61 | -- ^ meta.declaration.type.data meta.declaration.type.data.record.block keyword.operator.record.begin 62 | -- ^ meta.declaration.type.data meta.declaration.type.data.record.block keyword.operator.record.end 63 | -- ^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration meta.type-signature entity.name.type support.class.prelude.Int 64 | -- ^^^^ meta.declaration.type.data meta.declaration.type.data.record.block meta.record-field.type-declaration entity.other.attribute-name 65 | -- ^^^^ meta.declaration.type.data entity.name.tag 66 | -- ^^^^ meta.declaration.type.data meta.type-signature entity.name.type 67 | 68 | f :: a 69 | f = undefined 70 | -- >> =source.haskell 71 | -------------------------------------------------------------------------------- /spec/fixture/signatures.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | 3 | {-# LANGUAGE TypeOperators, ScopedTypeVariables #-} 4 | 5 | module Test where 6 | 7 | data Type = Type 8 | data OtherType = OtherType 9 | type (<--) a b = a b 10 | 11 | -- it parses newline declarations 12 | function :: Type -> OtherType 13 | -- ^^^^^^^^^ meta.function.type-declaration meta.type-signature entity.name.type 14 | -- ^^ meta.function.type-declaration meta.type-signature keyword.other.arrow 15 | -- ^^^^ meta.function.type-declaration meta.type-signature entity.name.type 16 | -- ^^^^^^^^^^^^^^^^^^ meta.function.type-declaration meta.type-signature 17 | -- ^^ meta.function.type-declaration keyword.other.double-colon 18 | -- <- meta.function.type-declaration 19 | function = undefined 20 | 21 | -- it parses in-line parenthesised declarations 22 | main :: IO () 23 | main = (putStrLn :: String -> IO ()) ("Hello World" :: String) 24 | -- ^^^^^^ meta.type-signature entity.name.type 25 | -- ^^ keyword.other.double-colon 26 | -- ^^^^^^^^^^^^^ string.quoted.double 27 | -- ^^ meta.type-signature constant.language.unit 28 | -- ^^ meta.type-signature entity.name.type 29 | -- ^^ meta.type-signature keyword.other.arrow 30 | -- ^^^^^^ meta.type-signature entity.name.type 31 | -- ^^ keyword.other.double-colon 32 | -- ^^^^^^^^ identifier support.function.prelude.putStrLn 33 | -- ^ keyword.operator 34 | -- <- identifier 35 | 36 | -- it doesnt get confused by quoted :: 37 | smth1 :: String -> String 38 | smth1 var = ("x :: String -> IO ()" ++ var) 39 | -- ^^^^^^^^^^^^^^^^^^^^^^ string.quoted.double 40 | 41 | -- it parses in-line non-parenthesised declarations 42 | main2 :: IO () 43 | main2 = putStrLn "Hello World" :: IO () 44 | -- ^^ meta.type-signature constant.language.unit 45 | -- ^^ meta.type-signature entity.name.type support.class.prelude.IO 46 | -- ^^ keyword.other.double-colon 47 | -- ^^^^^^^^^^^^^ string.quoted.double 48 | -- ^^^^^^^^ identifier 49 | -- <- identifier 50 | 51 | 52 | --------------------------- regression test for 71 --------------------------- 53 | 54 | regressionTest71 :: IO () 55 | regressionTest71 = do 56 | -- it stops parsing on <- 57 | xx :: String <- undefined 58 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 59 | -- ^^ keyword.operator 60 | -- ^^^^^^ meta.type-signature entity.name.type support.class.prelude.String 61 | -- ^^ keyword.other.double-colon 62 | -- ^^ identifier 63 | xx :: {- comment -} String <- undefined 64 | -- ^^ keyword.operator 65 | -- ^^^^^^^^^^^^^ meta.type-signature comment.block 66 | -- ^^ identifier 67 | xx :: {- comment String <- undefined 68 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comment.block 69 | -- ^^ meta.function.type-declaration entity.name.function 70 | -} 71 | return () 72 | where 73 | -- it stops parsing on = 74 | yy :: String = undefined 75 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 76 | -- ^ keyword.operator.assignment 77 | -- ^^^^^^ meta.type-signature entity.name.type support.class.prelude.String 78 | -- ^^ keyword.other.double-colon 79 | -- ^^ identifier 80 | -- it still works for type-operator signatures 81 | smth :: a <-- b 82 | -- ^^^^^^^^^^^^^^^ meta.function.type-declaration 83 | -- ^^^^ entity.name.function 84 | -- ^^ keyword.other.double-colon 85 | -- ^^^^^^^^ meta.function.type-declaration meta.type-signature 86 | -- ^ variable.other.generic-type 87 | -- ^^^ keyword.operator 88 | -- ^ variable.other.generic-type 89 | smth = undefined 90 | 91 | -- >> =source.haskell 92 | -------------------------------------------------------------------------------- /spec/fixture/type-app.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | 3 | {-# LANGUAGE TypeApplications #-} 4 | 5 | module Test where 6 | 7 | f = undefined @Char undefined 8 | -- ^^^^^ other.type-application 9 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 10 | f = undefined @[] undefined 11 | -- ^^^ other.type-application 12 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 13 | f = undefined @'[] undefined 14 | -- ^^^^ other.type-application 15 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 16 | f = undefined @'(Char, String) undefined 17 | -- ^^^^^^^^^^^^^^^^ other.type-application 18 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 19 | f = undefined @'() undefined 20 | -- ^^^^ other.type-application 21 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 22 | f = undefined @"asd" undefined 23 | -- ^^^^^^ other.type-application 24 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 25 | f = undefined @'a' undefined 26 | -- ^^^^^ other.type-application 27 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 28 | f = undefined @1 undefined 29 | -- ^^^ other.type-application 30 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 31 | f = undefined @(forall a. Show a => a -> String) undefined 32 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ other.type-application 33 | -- ^^^^^^^^^ identifier support.function.prelude.undefined 34 | 35 | f x@(x:xs) = _ 36 | -- ^^^^^^^ !other.type-application 37 | f x@'a' = _ 38 | -- ^^^ !other.type-application 39 | 40 | -- >> =source.haskell 41 | -------------------------------------------------------------------------------- /spec/fixture/type-export.hs: -------------------------------------------------------------------------------- 1 | module Test 2 | ( (:-)(), apply, 3 | -- ^^^^^ meta.declaration.module meta.declaration.exports entity.name.function 4 | -- ^^ meta.declaration.module meta.declaration.exports meta.other.constructor-list 5 | -- ^^^^ meta.declaration.module meta.declaration.exports entity.name.function.operator 6 | Type (..) 7 | -- ^^ meta.declaration.module meta.declaration.exports meta.other.constructor-list keyword.operator.wildcard 8 | ) where 9 | -------------------------------------------------------------------------------- /spec/fixture/typeFamilies.hs: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "source.haskell" 2 | {-# LANGUAGE PolyKinds, DataKinds, TypeOperators, TypeFamilies, UndecidableInstances #-} 3 | 4 | module Type.List where 5 | 6 | import GHC.TypeLits 7 | 8 | data Lst (l :: [k]) 9 | 10 | type family FromLst a where FromLst (Lst l) = l 11 | -- <- meta.declaration.type.type keyword.other.type 12 | -- ^ variable.other.generic-type 13 | -- ^ keyword.operator.assignment 14 | -- ^ variable.other.generic-type 15 | -- ^^^ entity.name.type 16 | -- ^^^^^^^ entity.name.type 17 | -- ^^^^^ keyword.other 18 | -- ^ variable.other.generic-type 19 | -- ^^^^^^^ entity.name.type 20 | -- ^^^^^^ keyword.other 21 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.type meta.type-signature 22 | 23 | type family Removed (el :: e) (cont :: c) :: l 24 | -- <- meta.declaration.type.type keyword.other.type 25 | -- ^ variable.other.generic-type 26 | -- ^ variable.other.generic-type 27 | -- ^^ keyword.operator 28 | -- ^^^^ variable.other.generic-type 29 | -- ^^ keyword.operator 30 | -- ^^ variable.other.generic-type 31 | -- ^^^^^^^ entity.name.type 32 | -- ^^^^^^ keyword.other 33 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.type meta.type-signature 34 | type family RemovedIdx (idx :: Nat) (cont :: c) :: l 35 | -- ^^^ meta.declaration.type.type meta.type-signature entity.name.type 36 | type family ElAt (idx :: Nat) (cont :: c) :: l 37 | -- ^^^ meta.declaration.type.type meta.type-signature entity.name.type 38 | 39 | type family Reverse' lst lst' where 40 | -- ^^^^^ keyword.other 41 | -- ^^^^ variable.other.generic-type 42 | -- ^^^ variable.other.generic-type 43 | -- ^^^^^^^^ entity.name.type 44 | -- ^^^^^^ keyword.other 45 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.type meta.type-signature 46 | Reverse' '[] lst = lst 47 | -- ^^^ variable.other.generic-type 48 | -- ^^^ variable.other.generic-type 49 | -- ^^^^^^^^ entity.name.type 50 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.type meta.type-signature 51 | Reverse' (l ': ls) lst = Reverse' ls (l ': lst) 52 | -- ^^ other.promoted 53 | -- ^ keyword.operator 54 | -- ^^ other.promoted 55 | -- ^ keyword.operator 56 | -- ^^^ variable.other.generic-type 57 | -- ^^ variable.other.generic-type 58 | -- ^^^^^^^^ entity.name.type 59 | -- ^^^ variable.other.generic-type 60 | -- ^^ variable.other.generic-type 61 | -- ^^^^^^^^ entity.name.type 62 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.type meta.type-signature 63 | 64 | type family Zip2 (l1 :: [*]) (l2 :: [*]) :: [*] where 65 | -- ^^^^^ keyword.other 66 | -- ^^ variable.other.generic-type 67 | -- ^^ variable.other.generic-type 68 | -- ^^^^ entity.name.type 69 | -- ^^^^^^ keyword.other 70 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.type meta.type-signature 71 | Zip2 (x1 ': xs1) (x2 ': xs2) = (x1,x2) ': Zip2 xs1 xs2 72 | -- ^^^ variable.other.generic-type 73 | -- ^^^ variable.other.generic-type 74 | -- ^^^^ entity.name.type 75 | -- ^^ variable.other.generic-type 76 | -- ^^ variable.other.generic-type 77 | -- ^^^ variable.other.generic-type 78 | -- ^^ variable.other.generic-type 79 | -- ^^^ variable.other.generic-type 80 | -- ^^ variable.other.generic-type 81 | -- ^^^^ entity.name.type 82 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.type meta.type-signature 83 | Zip2 l1 l2 = '[] 84 | -- ^^ meta.declaration.type.type meta.type-signature variable.other.generic-type 85 | -- ^^ meta.declaration.type.type meta.type-signature variable.other.generic-type 86 | -- ^^^^ meta.declaration.type.type meta.type-signature entity.name.type 87 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.type.type meta.type-signature 88 | 89 | type family PromConstr a where 90 | PromConstr ('Just a) = 1 91 | -- ^^^^^ other.promoted 92 | -- ^^^^ entity.name.type 93 | -- ^^^^ support.class.prelude.Just 94 | -- ^ constant.numeric.decimal 95 | PromConstr 'Nothing = 2 96 | -- ^ constant.numeric.decimal 97 | -- ^^^^^^^ entity.name.type support.class.prelude.Nothing 98 | -- ^^^^^^^^ other.promoted 99 | 100 | type family PromConstr2 a where 101 | PromConstr2 '() = 2 102 | -- ^^^ other.promoted 103 | -- ^^ constant.language.unit 104 | 105 | type family PromConstr3 a where 106 | PromConstr3 '[] = 2 107 | -- ^^ constant.language.empty-list 108 | -- ^^^ other.promoted 109 | -------------------------------------------------------------------------------- /spec/grammar-test-spec.coffee: -------------------------------------------------------------------------------- 1 | grammarTest = require 'atom-grammar-test' 2 | {sep} = require 'path' 3 | 4 | describe 'Fixture-based grammar tests', -> 5 | 6 | beforeEach -> 7 | waitsForPromise -> 8 | atom.packages.activatePackage 'language-haskell' 9 | 10 | test = (name, file, desc = describe) -> 11 | desc name, -> grammarTest("#{__dirname}#{sep}fixture#{sep}#{file}") 12 | ftest = (name, file) -> test(name, file, fdescribe) 13 | 14 | test 'Haskell', 'general.hs' 15 | test 'Liquid Haskell', 'liquidhaskell.hs' 16 | test 'Record syntax', 'record.hs' 17 | test 'Identifiers', 'identifiers.hs' 18 | test 'GADTs', 'gadt.hs' 19 | test 'Multiline signatures', 'multilineSignatures.hs' 20 | test 'Signatures', 'signatures.hs' 21 | test 'Type families', 'typeFamilies.hs' 22 | test 'Deriving strategy', 'deriveStrategy.hs' 23 | test 'Char Kind', 'charKind.hs' 24 | -------------------------------------------------------------------------------- /spec/language-haskell-multiline-sigs-spec.coffee: -------------------------------------------------------------------------------- 1 | {grammarExpect, customMatchers} = require './util' 2 | 3 | describe "Language-Haskell multiline signatures", -> 4 | grammar = null 5 | lhs = null 6 | 7 | testBoth = (ex, test) -> 8 | it "works in Haskell", -> 9 | test(grammar, ex) 10 | it "works in Literate Haskell", -> 11 | test(lhs, '> ' + ex.split('\n').join('\n> ')) 12 | 13 | beforeEach -> 14 | @addMatchers(customMatchers) 15 | waitsForPromise -> 16 | atom.packages.activatePackage("language-haskell") 17 | 18 | runs -> 19 | grammar = atom.grammars.grammarForScopeName("source.haskell") 20 | lhs = atom.grammars.grammarForScopeName("text.tex.latex.haskell") 21 | 22 | topSigs = [ 23 | ''' 24 | someFunc 25 | :: String -> String 26 | ''' 27 | ''' 28 | someFunc 29 | :: String 30 | -> String 31 | ''' 32 | ''' 33 | someFunc 34 | :: forall res r (s :: * -> *) 35 | . ( s ~ Maybe 36 | , Monad s 37 | ) 38 | => Either res r 39 | -> Either [res] [r] 40 | ''' 41 | ] 42 | negativeTopSigs = [ 43 | # This one's supposed to fail 44 | ''' 45 | someFunc 46 | :: String 47 | -> Integer 48 | ''' 49 | ] 50 | doNotationScoped = [ 51 | ''' 52 | f x = do 53 | stuff :: String <- x 54 | return () 55 | ''' 56 | ''' 57 | g x = do 58 | stuff 59 | :: String 60 | -> String <- x 61 | return () 62 | ''' 63 | ''' 64 | g x = do 65 | stuff 66 | :: String 67 | -> a <- x 68 | return () 69 | ''' 70 | ''' 71 | h x = do 72 | stuff 73 | :: String 74 | ->> String 75 | --> IO String <- x 76 | return () 77 | ''' 78 | ] 79 | 80 | describe "Top level signatures", -> 81 | topSigs.forEach (ex) -> 82 | it "correctly gets line scopes for #{ex}", -> 83 | g = grammarExpect grammar, ex 84 | g.toHaveScopes [['identifier.haskell']].concat( 85 | [2..ex.split('\n').length].map -> 86 | ['meta.multiline.type-declaration.haskell'] 87 | ) 88 | topSigs.forEach (ex) -> 89 | describe "parses #{ex}", -> 90 | testBoth ex, (grammar, ex) -> 91 | g = grammarExpect grammar, ex 92 | # g.toHaveScopes [['identifier.haskell']].concat( 93 | # [2..ex.split('\n').length].map -> 94 | # ['meta.multiline.type-declaration.haskell'] 95 | # ) 96 | g.tokensToHaveScopes( 97 | 'String': ['entity.name.type.haskell'] 98 | 'someFunc': ['identifier.haskell'] 99 | ) 100 | describe "Top level signatures failures", -> 101 | negativeTopSigs.forEach (ex) -> 102 | describe "does not parse #{ex}", -> 103 | testBoth ex, (grammar, ex) -> 104 | g = grammarExpect grammar, ex 105 | # g.notToHaveScopes [['identifier.haskell']].concat( 106 | # [2..ex.split('\n').length].map -> 107 | # ['meta.multiline.type-declaration.haskell'] 108 | # ) 109 | g.tokensToHaveScopes( 110 | 'String': ['entity.name.type.haskell'] 111 | 'someFunc': ['identifier.haskell'] 112 | ) 113 | g.tokensNotToHaveScopes('Integer': ['entity.name.type.haskell']) 114 | describe "Scoped do notation signatures", -> 115 | doNotationScoped.forEach (ex) -> 116 | describe "parses #{ex}", -> 117 | testBoth ex, (grammar, ex) -> 118 | g = grammarExpect grammar, ex 119 | g.tokensToHaveScopes( 120 | 'String': ['entity.name.type.haskell'] 121 | 'IO': ['entity.name.type.haskell'] 122 | 'x': ['identifier.haskell'] 123 | 'a': ['variable.other.generic-type.haskell'] 124 | ) 125 | g.tokensNotToHaveScopes( 126 | 'x': ['entity.name.type.haskell', 'variable.other.generic-type.haskell'] 127 | 'a': ['identifier.haskell'] 128 | ) 129 | -------------------------------------------------------------------------------- /spec/language-haskell-numbers-spec.coffee: -------------------------------------------------------------------------------- 1 | {grammarExpect, customMatchers} = require './util' 2 | 3 | describe "Language-Haskell Numbers", -> 4 | grammar = null 5 | 6 | beforeEach -> 7 | @addMatchers(customMatchers) 8 | waitsForPromise -> 9 | atom.packages.activatePackage("language-haskell") 10 | 11 | runs -> 12 | grammar = atom.grammars.grammarForScopeName("source.haskell") 13 | 14 | describe "numbers", -> 15 | testNumbers = (scope, s) -> 16 | g = grammarExpect grammar, s 17 | tokens = s.split("\n").map((s) -> [s]) 18 | g.toHaveTokens tokens 19 | g.toHaveScopes ( 20 | [scope] for i in tokens 21 | ) 22 | testNumbersNeg = (s, scope) -> 23 | g = grammarExpect grammar, s 24 | tokens = s.split("\n").map((s) -> [s]) 25 | g.notToHaveScopes ( 26 | [scope] for i in tokens 27 | ) 28 | 29 | it "parses decimal integers", -> 30 | testNumbers 'constant.numeric.decimal.haskell', ''' 31 | 12345 32 | 123_456 33 | 123_45__6 34 | ''' 35 | 36 | it "parses binary integers", -> 37 | testNumbers 'constant.numeric.binary.haskell', ''' 38 | 0b10010101 39 | 0b1001_0101 40 | 0b100__1_0101 41 | ''' 42 | 43 | it "parses hexadecimal integers", -> 44 | testNumbers 'constant.numeric.hexadecimal.haskell', ''' 45 | 0xfade145 46 | 0xfad_e145 47 | 0xf__ad_e14_5 48 | ''' 49 | 50 | it "parses octal integers", -> 51 | testNumbers 'constant.numeric.octal.haskell', ''' 52 | 0o1234567 53 | 0o1_2_3___4_5_6_7 54 | ''' 55 | 56 | it "does not parse invalid decimal integers", -> 57 | testNumbersNeg 'constant.numeric.decimal.haskell', ''' 58 | 12345a 59 | 123_456a 60 | ''' 61 | 62 | it "does not parse invalid binary integers", -> 63 | testNumbersNeg 'constant.numeric.binary.haskell', ''' 64 | 0b1001010123 65 | 0b100101_0123 66 | ''' 67 | 68 | it "does not parse invalid hexadecimal integers", -> 69 | testNumbersNeg 'constant.numeric.hexadecimal.haskell', ''' 70 | 0xfade145z 71 | 0xfade14_5z 72 | ''' 73 | 74 | it "does not parse invalid octal integers", -> 75 | testNumbersNeg 'constant.numeric.octal.haskell', ''' 76 | 0o12345678 77 | 0o123_45678 78 | ''' 79 | 80 | it "parses floating point numbers", -> 81 | testNumbers 'constant.numeric.float.haskell', ''' 82 | 1.234 83 | 1e23 84 | 1.23e4 85 | 1E23 86 | 1.23E4 87 | 1.2_34 88 | 1e2_3 89 | 1.23e4 90 | 1_2E2_3 91 | 1.2_3E4 92 | ''' 93 | 94 | it "parses hexfloat numbers", -> 95 | testNumbers 'constant.numeric.hexfloat.haskell', ''' 96 | 0x1.234 97 | 0x1p23 98 | 0x1.23p4 99 | 0x1P23 100 | 0x1.23P4 101 | 0xa.Efa 102 | 0xFap23 103 | 0xf.23p4 104 | 0x1P23 105 | 0XaP23 106 | 0X1.23P4 107 | 0xa.E_fa 108 | 0xF_ap2_3 109 | 0xf.2_3p4 110 | 0x1P2_3 111 | 0XaP2_3 112 | 0X1.2_3P4 113 | ''' 114 | -------------------------------------------------------------------------------- /spec/language-haskell-spec.coffee: -------------------------------------------------------------------------------- 1 | {grammarExpect, customMatchers} = require './util' 2 | 3 | describe "Language-Haskell", -> 4 | grammar = null 5 | 6 | beforeEach -> 7 | @addMatchers(customMatchers) 8 | waitsForPromise -> 9 | atom.packages.activatePackage("language-haskell") 10 | 11 | runs -> 12 | grammar = atom.grammars.grammarForScopeName("source.haskell") 13 | 14 | it "parses the grammar", -> 15 | expect(grammar).toBeTruthy() 16 | expect(grammar.scopeName).toBe "source.haskell" 17 | 18 | describe "chars", -> 19 | it 'tokenizes general chars', -> 20 | chars = ['a', '0', '9', 'z', '@', '0', '"'] 21 | 22 | for scope, char of chars 23 | g = grammarExpect grammar, "'#{char}'" 24 | g.toHaveTokens [["'", char, "'"]] 25 | g.toHaveScopes [['source.haskell', "string.quoted.single.haskell"]] 26 | g.tokenToHaveScopes [ 27 | 0: ['punctuation.definition.string.begin.haskell'] 28 | 2: ['punctuation.definition.string.end.haskell'] 29 | ] 30 | 31 | it 'tokenizes escape chars', -> 32 | escapeChars = ['\\t', '\\n', '\\\''] 33 | for scope, char of escapeChars 34 | g = grammarExpect grammar, "'#{char}'" 35 | g.toHaveTokens [["'", char, "'"]] 36 | g.toHaveScopes [['source.haskell', "string.quoted.single.haskell"]] 37 | g.tokenToHaveScopes [ 38 | 0: ['punctuation.definition.string.begin.haskell'] 39 | 1: ['constant.character.escape.haskell'] 40 | 2: ['punctuation.definition.string.end.haskell'] 41 | ] 42 | it 'tokenizes control chars', -> 43 | escapeChars = [64..95].map (x) -> "\\^#{String.fromCharCode(x)}" 44 | for scope, char of escapeChars 45 | g = grammarExpect grammar, "'#{char}'" 46 | g.toHaveTokens [["'", char, "'"]] 47 | g.toHaveScopes [['source.haskell', "string.quoted.single.haskell"]] 48 | g.tokenToHaveScopes [1: ["constant.character.escape.control.haskell"]] 49 | 50 | describe "keywords", -> 51 | { controlKeywords, otherKeywords } = require '../src/include/util' 52 | 53 | controlKeywords.forEach (keyword) -> 54 | it "tokenizes #{keyword} as a control keyword", -> 55 | g = grammarExpect grammar, keyword 56 | g.toHaveTokens [[keyword]] 57 | g.toHaveScopes [["keyword.control.#{keyword}.haskell"]] 58 | 59 | otherKeywords.forEach (keyword) -> 60 | it "tokenizes #{keyword} as a keyword", -> 61 | g = grammarExpect grammar, keyword 62 | g.toHaveTokens [[keyword]] 63 | g.toHaveScopes [["keyword.other.#{keyword}.haskell"]] 64 | 65 | ['infix', 'infixl', 'infixr'].forEach (keyword) -> 66 | it "tokenizes #{keyword} as a keyword", -> 67 | g = grammarExpect grammar, keyword 68 | g.toHaveTokens [[keyword]] 69 | g.toHaveScopes [["keyword.operator.#{keyword}.haskell"]] 70 | 71 | describe "Prelude", -> 72 | prelude = require '../src/include/prelude' 73 | # classes,funct,constr,types,operators 74 | test = (what, template, tokens, scope) -> 75 | describe what, -> 76 | prelude[what].forEach (x) -> 77 | it "handles #{what} #{x}", -> 78 | g = grammarExpect grammar, template(x) 79 | g.toHaveTokens [tokens(x)] 80 | g.tokenToHaveScopes [scope(x)] 81 | test "classes", 82 | (x) -> "func :: #{x} a => a", 83 | (x) -> ['func', ' ', '::', ' ', x, ' ', 'a', ' ', '=>', ' ', 'a'] 84 | (x) -> 4: ["entity.name.type.haskell", "entity.other.inherited-class.prelude.#{x}.haskell"] 85 | test "funct", 86 | (x) -> "#{x}", 87 | (x) -> [x] 88 | (x) -> 0: ["identifier.haskell", "support.function.prelude.#{x}.haskell"] 89 | test "constr", 90 | (x) -> "#{x}", 91 | (x) -> [x] 92 | (x) -> 0: ["entity.name.tag.haskell", "support.tag.prelude.#{x}.haskell"] 93 | test "types", 94 | (x) -> "type Test = #{x}", 95 | (x) -> ['type', ' ', 'Test', ' ', '=', ' ', x] 96 | (x) -> 6: ["entity.name.type.haskell", "support.class.prelude.#{x}.haskell"] 97 | # operators are handled separately 98 | 99 | describe "identifiers", -> 100 | it 'doesnt highlight partial prelude names', -> 101 | g = grammarExpect(grammar, "top'n'tail") 102 | g.toHaveScopes [['source.haskell', 'identifier.haskell']] 103 | g.toHaveTokens [["top'n'tail"]] 104 | g.tokensToHaveScopes { 105 | "top'n'tail": ['identifier.haskell'] 106 | } 107 | 108 | describe ':: declarations', -> 109 | it 'parses newline declarations', -> 110 | g = grammarExpect(grammar, 'function :: Type -> OtherType') 111 | g.toHaveScopes [['source.haskell', 'meta.function.type-declaration.haskell']] 112 | g.toHaveTokens [[ 'function', ' ', '::', ' ', 'Type', ' ', '->', ' ', 'OtherType' ]] 113 | g.tokensToHaveScopes { 114 | 'function': ['entity.name.function.haskell'] 115 | '::': ['keyword.other.double-colon.haskell'] 116 | 'Type': ['entity.name.type.haskell'] 117 | '->': ['keyword.other.arrow.haskell'] 118 | 'OtherType': ['entity.name.type.haskell'] 119 | } 120 | 121 | it 'parses in-line parenthesised declarations', -> 122 | g = grammarExpect(grammar, 'main = (putStrLn :: String -> IO ()) ("Hello World" :: String)') 123 | g.toHaveScopes [['source.haskell']] 124 | g.toHaveTokens [[ 125 | "main", " ", "=", " ", "(", "putStrLn", " ", "::", " ", "String", " ", 126 | "->", " ", "IO", " ", "()", ")", " ", "(", "\"", "Hello World", "\"", 127 | " ", "::", " ", "String", ")" 128 | ]] 129 | g.tokensToHaveScopes { 130 | "main" : ['identifier.haskell'] 131 | "=" : ['keyword.operator.haskell'] 132 | "putStrLn" : ['support.function.prelude.putStrLn.haskell' ] 133 | "::" : ['keyword.other.double-colon.haskell'] 134 | "String" : ['entity.name.type.haskell', 'support.class.prelude.String.haskell'] 135 | "->" : ['keyword.other.arrow.haskell'] 136 | "IO" : ['entity.name.type.haskell', 'support.class.prelude.IO.haskell'] 137 | "()" : ['constant.language.unit.haskell' ] 138 | "Hello World" : ['string.quoted.double.haskell'] 139 | } 140 | 141 | it 'doesnt get confused by quoted ::', -> 142 | g = grammarExpect(grammar, '("x :: String -> IO ()" ++ var)') 143 | g.toHaveScopes [['source.haskell']] 144 | g.toHaveTokens [[ "(", "\"", "x :: String -> IO ()", "\"", " ", "++", " ", "var", ")"]] 145 | g.tokensToHaveScopes { 146 | "x :: String -> IO ()" : ['string.quoted.double.haskell'] 147 | "++" : ['keyword.operator.haskell'] 148 | "var" : ['identifier.haskell'] 149 | } 150 | 151 | it 'parses in-line non-parenthesised declarations', -> 152 | g = grammarExpect(grammar, 'main = putStrLn "Hello World" :: IO ()') 153 | g.toHaveScopes [['source.haskell']] 154 | g.toHaveTokens [[ 155 | 'main', ' ', '=', ' ', 'putStrLn', ' ', '"', 'Hello World', '"', ' ', '::', ' ', 'IO', ' ', '()' 156 | ]] 157 | g.tokensToHaveScopes { 158 | 'main' : [ 'identifier.haskell' ] 159 | '=' : [ 'keyword.operator.haskell' ] 160 | 'putStrLn' : [ 'identifier.haskell', 'support.function.prelude.putStrLn.haskell' ] 161 | '"' : [ 'string.quoted.double.haskell' ] 162 | 'Hello World' : [ 'string.quoted.double.haskell' ] 163 | '::' : [ 'keyword.other.double-colon.haskell' ] 164 | 'IO' : [ 'meta.type-signature.haskell', 'entity.name.type.haskell', 'support.class.prelude.IO.haskell' ] 165 | '()' : [ 'meta.type-signature.haskell', 'constant.language.unit.haskell' ] 166 | } 167 | g.tokenToHaveScopes [ 168 | 6: [ 'punctuation.definition.string.begin.haskell' ] 169 | 8: [ 'punctuation.definition.string.end.haskell' ] 170 | ] 171 | 172 | describe "type operators", -> 173 | it "parses type operators", -> 174 | data = ":: a *** b" 175 | {tokens} = grammar.tokenizeLine(data) 176 | expect(tokens[4].value).toEqual '***' 177 | expect(tokens[4].scopes).toContain 'keyword.operator.haskell' 178 | it "doesn't confuse arrows and type operators", -> 179 | g = grammarExpect(grammar, ":: a --> b") 180 | g.toHaveTokens [['::', ' ', 'a', ' ', '-->', ' ', 'b']] 181 | g.toHaveScopes [['source.haskell']] 182 | g.tokenToHaveScopes [4: ['keyword.operator.haskell', 'meta.type-signature.haskell']] 183 | 184 | g = grammarExpect(grammar, ":: a ->- b") 185 | g.toHaveTokens [['::', ' ', 'a', ' ', '->-', ' ', 'b']] 186 | g.toHaveScopes [['source.haskell']] 187 | g.tokenToHaveScopes [4: ['keyword.operator.haskell', 'meta.type-signature.haskell']] 188 | 189 | g = grammarExpect(grammar, ":: a ==> b") 190 | g.toHaveTokens [['::', ' ', 'a', ' ', '==>', ' ', 'b']] 191 | g.toHaveScopes [['source.haskell']] 192 | g.tokenToHaveScopes [4: ['keyword.operator.haskell', 'meta.type-signature.haskell']] 193 | 194 | g = grammarExpect(grammar, ":: a =>= b") 195 | g.toHaveTokens [['::', ' ', 'a', ' ', '=>=', ' ', 'b']] 196 | g.toHaveScopes [['source.haskell']] 197 | g.tokenToHaveScopes [4: ['keyword.operator.haskell', 'meta.type-signature.haskell']] 198 | 199 | describe "comments", -> 200 | it "parses block comments", -> 201 | g = grammarExpect grammar, "{- this is a block comment -}" 202 | g.toHaveTokens [['{-', ' this is a block comment ', '-}']] 203 | g.toHaveScopes [['source.haskell', 'comment.block.haskell']] 204 | g.tokenToHaveScopes [ 205 | 0: ['punctuation.definition.comment.block.start.haskell'] 206 | 2: ['punctuation.definition.comment.block.end.haskell'] 207 | ] 208 | 209 | it "parses nested block comments", -> 210 | g = grammarExpect grammar, "{- this is a {- nested -} block comment -}" 211 | g.toHaveTokens [['{-', ' this is a ', '{-', ' nested ', '-}', ' block comment ', '-}']] 212 | g.toHaveScopes [['source.haskell', 'comment.block.haskell']] 213 | g.tokenToHaveScopes [ 214 | 0: ['punctuation.definition.comment.block.start.haskell'] 215 | 2: ['punctuation.definition.comment.block.start.haskell'] 216 | 4: ['punctuation.definition.comment.block.end.haskell'] 217 | 6: ['punctuation.definition.comment.block.end.haskell'] 218 | ] 219 | 220 | it "parses pragmas as comments in block comments", -> 221 | g = grammarExpect grammar, '{- this is a {-# nested #-} block comment -}' 222 | g.toHaveTokens [['{-', ' this is a ', '{-', '# nested #', '-}', ' block comment ', '-}']] 223 | g.toHaveScopes [['source.haskell', 'comment.block.haskell']] 224 | g.tokenToHaveScopes [ 225 | 0: ['punctuation.definition.comment.block.start.haskell'] 226 | 2: ['punctuation.definition.comment.block.start.haskell'] 227 | 4: ['punctuation.definition.comment.block.end.haskell'] 228 | 6: ['punctuation.definition.comment.block.end.haskell'] 229 | ] 230 | 231 | describe "pragmas", -> 232 | it "parses pragmas", -> 233 | g = grammarExpect grammar, '{-# LANGUAGE OverloadedStrings #-}' 234 | g.toHaveTokens [['{-#', ' ', 'LANGUAGE', ' OverloadedStrings ', '#-}']] 235 | g.toHaveScopes [['source.haskell', 'meta.preprocessor.haskell']] 236 | g.tokenToHaveScopes [2: ['keyword.other.preprocessor.LANGUAGE.haskell']] 237 | 238 | it "parses lowercase pragmas", -> 239 | g = grammarExpect grammar, '{-# language OverloadedStrings #-}' 240 | g.toHaveTokens [['{-#', ' ', 'language', ' OverloadedStrings ', '#-}']] 241 | g.toHaveScopes [['source.haskell', 'meta.preprocessor.haskell']] 242 | g.tokenToHaveScopes [2: ['keyword.other.preprocessor.language.haskell']] 243 | 244 | it "parses mixed case pragmas", -> 245 | g = grammarExpect grammar, '{-# lanGuaGE OverloadedStrings #-}' 246 | g.toHaveTokens [['{-#', ' ', 'lanGuaGE', ' OverloadedStrings ', '#-}']] 247 | g.toHaveScopes [['source.haskell', 'meta.preprocessor.haskell']] 248 | g.tokenToHaveScopes [2: ['keyword.other.preprocessor.lanGuaGE.haskell']] 249 | 250 | describe "instance", -> 251 | it "recognizes instances", -> 252 | g = grammarExpect grammar, 'instance Class where' 253 | g.toHaveTokens [['instance', ' ', 'Class', ' ', 'where']] 254 | g.toHaveScopes [['source.haskell', 'meta.declaration.instance.haskell']] 255 | g.tokenToHaveScopes [ 256 | 1: ['meta.type-signature.haskell'] 257 | 2: ['meta.type-signature.haskell', 'entity.name.type.haskell'] 258 | 3: ['meta.type-signature.haskell'] 259 | 4: ['keyword.other.haskell'] 260 | ] 261 | it "recognizes instance pragmas", -> 262 | for p in [ 'OVERLAPS', 'OVERLAPPING', 'OVERLAPPABLE', 'INCOHERENT' ] 263 | g = grammarExpect grammar, "instance {-# #{p} #-} Class where" 264 | g.toHaveTokens [['instance', ' ', '{-#', ' ', p, ' ', '#-}', ' ', 'Class', ' ', 'where']] 265 | g.toHaveScopes [['source.haskell', 'meta.declaration.instance.haskell']] 266 | g.tokenToHaveScopes [ 267 | 2: ['meta.preprocessor.haskell'] 268 | 3: ['meta.preprocessor.haskell'] 269 | 4: ['meta.preprocessor.haskell', "keyword.other.preprocessor.#{p}.haskell"] 270 | 5: ['meta.preprocessor.haskell'] 271 | 6: ['meta.preprocessor.haskell'] 272 | ] 273 | 274 | it "recognizes lowercase instance pragmas", -> 275 | for p in [ 'overlaps', 'overlapping', 'overlappable', 'incoherent' ] 276 | g = grammarExpect grammar, "instance {-# #{p} #-} Class where" 277 | g.toHaveTokens [['instance', ' ', '{-#', ' ', p, ' ', '#-}', ' ', 'Class', ' ', 'where']] 278 | g.toHaveScopes [['source.haskell', 'meta.declaration.instance.haskell']] 279 | g.tokenToHaveScopes [ 280 | 2: ['meta.preprocessor.haskell'] 281 | 3: ['meta.preprocessor.haskell'] 282 | 4: ['meta.preprocessor.haskell', "keyword.other.preprocessor.#{p}.haskell"] 283 | 5: ['meta.preprocessor.haskell'] 284 | 6: ['meta.preprocessor.haskell'] 285 | ] 286 | describe "module", -> 287 | it "understands module declarations", -> 288 | g = grammarExpect grammar, 'module Module where' 289 | g.toHaveTokens [['module', ' ', 'Module', ' ', 'where']] 290 | g.toHaveScopes [['source.haskell', 'meta.declaration.module.haskell']] 291 | g.tokenToHaveScopes [2: ['support.other.module.haskell']] 292 | it "understands module declarations with exports", -> 293 | g = grammarExpect grammar, 'module Module (export1, export2) where' 294 | g.toHaveTokens [['module', ' ', 'Module', ' ', '(', 'export1', ',', ' ', 'export2', ')', ' ', 'where']] 295 | g.toHaveScopes [['source.haskell', 'meta.declaration.module.haskell']] 296 | g.tokenToHaveScopes [ 297 | 2: ['support.other.module.haskell'] 298 | 5: ['meta.declaration.exports.haskell', 'entity.name.function.haskell'] 299 | 8: ['meta.declaration.exports.haskell', 'entity.name.function.haskell'] 300 | ] 301 | it "understands module declarations with operator exports", -> 302 | g = grammarExpect grammar, 'module Module ((<|>), export2) where' 303 | g.toHaveTokens [['module', ' ', 'Module', ' ', '(', '(<|>)', ',', ' ', 'export2', ')', ' ', 'where']] 304 | g.toHaveScopes [['source.haskell', 'meta.declaration.module.haskell']] 305 | g.tokenToHaveScopes [ 306 | 2: ['support.other.module.haskell'] 307 | 5: ['meta.declaration.exports.haskell', 'entity.name.function.operator.haskell'] 308 | 8: ['meta.declaration.exports.haskell', 'entity.name.function.haskell'] 309 | ] 310 | it "understands module declarations with export lists", -> 311 | g = grammarExpect grammar, 'module Module (export1 (..), export2 (Something)) where' 312 | g.toHaveTokens [['module', ' ', 'Module', ' ', '(', 'export1', ' ', '(' , '..', ')', 313 | ',', ' ', 'export2', ' ', '(', 'Something', ')', ')', ' ', 'where']] 314 | g.toHaveScopes [['source.haskell', 'meta.declaration.module.haskell']] 315 | g.tokenToHaveScopes [ 316 | 2: ['support.other.module.haskell'] 317 | 5: ['meta.declaration.exports.haskell', 'entity.name.function.haskell'] 318 | 8: ['meta.declaration.exports.haskell', 'meta.other.constructor-list.haskell', 319 | 'keyword.operator.wildcard.haskell'] 320 | 12: ['meta.declaration.exports.haskell', 'entity.name.function.haskell'] 321 | 15: ['meta.declaration.exports.haskell', 'meta.other.constructor-list.haskell', 322 | 'entity.name.tag.haskell'] 323 | ] 324 | describe "regression test for comments after module name in imports", -> 325 | it "parses comments after module names", -> 326 | g = grammarExpect grammar, 'import Module -- comment' 327 | g.toHaveTokens [['import', ' ', 'Module', ' ', '--', ' comment']] 328 | g.toHaveScopes [['source.haskell', 'meta.import.haskell']] 329 | g.tokenToHaveScopes [ 330 | 2: ['support.other.module.haskell'] 331 | 4: ['comment.line.double-dash.haskell', 'punctuation.definition.comment.haskell'] 332 | 5: ['comment.line.double-dash.haskell'] 333 | ] 334 | 335 | describe "quasiqotes", -> 336 | it "parses unqualified quasiquotes", -> 337 | g = grammarExpect grammar, '[q| do maybe String|]' 338 | g.toHaveTokens [['[', 'q', '|', ' do maybe String', '|', ']']] 339 | g.toHaveScopes [['source.haskell']] 340 | g.tokenToHaveScopes [ 341 | 0: ['punctuation.definition.quasiquotes.begin.haskell'] 342 | 1: ['entity.name.tag.haskell'] 343 | 3: ['quoted.quasiquotes.qq-q.haskell'] 344 | 5: ['punctuation.definition.quasiquotes.end.haskell'] 345 | ] 346 | 347 | it "parses qualified quasiquotes", -> 348 | g = grammarExpect grammar, '[Some.Module.Name.q| do maybe String|]' 349 | g.toHaveTokens [['[', 'Some.Module.Name.', 'q', '|', ' do maybe String', '|', ']']] 350 | g.toHaveScopes [['source.haskell']] 351 | g.tokenToHaveScopes [ 352 | 0: ['punctuation.definition.quasiquotes.begin.haskell'] 353 | 1: ['entity.name.tag.haskell', 'support.other.module.haskell'] 354 | 2: ['entity.name.tag.haskell'] 355 | 4: ['quoted.quasiquotes.qq-q.haskell'] 356 | 6: ['punctuation.definition.quasiquotes.end.haskell'] 357 | ] 358 | -------------------------------------------------------------------------------- /spec/literate-haskell-spec.coffee: -------------------------------------------------------------------------------- 1 | {grammarExpect, customMatchers} = require './util' 2 | 3 | describe "Literate Haskell", -> 4 | grammar = null 5 | 6 | beforeEach -> 7 | @addMatchers(customMatchers) 8 | waitsForPromise -> 9 | atom.packages.activatePackage("language-haskell") 10 | 11 | runs -> 12 | grammar = atom.grammars.grammarForScopeName("text.tex.latex.haskell") 13 | 14 | it "parses the grammar", -> 15 | expect(grammar).toBeTruthy() 16 | expect(grammar.scopeName).toBe "text.tex.latex.haskell" 17 | 18 | describe "regression test for 64", -> 19 | it "parses inline signatures", -> 20 | g = grammarExpect grammar, 'a signature |f::Type| should be contained' 21 | g.toHaveTokens [['a signature ', '|', 'f', '::', 'Type', '|', ' should be contained']] 22 | g.toHaveScopes [['text.tex.latex.haskell']] 23 | g.tokenToHaveScopes [ 24 | 1: ['meta.embedded.text.haskell.latex.haskell'] 25 | 2: ['meta.embedded.text.haskell.latex.haskell', 'meta.function.type-declaration.haskell'] 26 | 3: ['meta.embedded.text.haskell.latex.haskell', 'keyword.other.double-colon.haskell'] 27 | 4: ['meta.embedded.text.haskell.latex.haskell' 28 | , 'meta.type-signature.haskell' 29 | , 'entity.name.type.haskell'] 30 | 5: ['meta.embedded.text.haskell.latex.haskell'] 31 | ] 32 | it "parses inline signatures with dots", -> 33 | g = grammarExpect grammar, 'a signature |f::Type|. should be contained' 34 | g.toHaveTokens [['a signature ', '|', 'f', '::', 'Type', '|', '. should be contained']] 35 | g.toHaveScopes [['text.tex.latex.haskell']] 36 | g.tokenToHaveScopes [ 37 | 1: ['meta.embedded.text.haskell.latex.haskell'] 38 | 2: ['meta.embedded.text.haskell.latex.haskell', 'meta.function.type-declaration.haskell'] 39 | 3: [ 'meta.embedded.text.haskell.latex.haskell' 40 | , 'keyword.other.double-colon.haskell'] 41 | 4: ['meta.embedded.text.haskell.latex.haskell' 42 | , 'meta.type-signature.haskell' 43 | , 'entity.name.type.haskell'] 44 | 5: ['meta.embedded.text.haskell.latex.haskell'] 45 | ] 46 | it "parses inline code with pipes", -> 47 | g = grammarExpect grammar, 'a code |type Bool = True || False| should parse correctly' 48 | g.toHaveTokens [['a code ', '|', 'type', ' ', 'Bool', ' ', '=', ' ', 'True', ' ' 49 | , '||', ' ', 'False', '|', ' should parse correctly']] 50 | g.toHaveScopes [['text.tex.latex.haskell']] 51 | g.tokenToHaveScopes [ 52 | 1: ['meta.embedded.text.haskell.latex.haskell'] 53 | 2: ["keyword.other.type.haskell"] 54 | 3: ["meta.type-signature.haskell"] 55 | 4: ["entity.name.type.haskell"] 56 | 6: ['keyword.operator.assignment.haskell'] 57 | 8: ['entity.name.type.haskell'] 58 | 10: ['keyword.operator.haskell'] 59 | 12: ['entity.name.type.haskell'] 60 | 13: ['meta.embedded.text.haskell.latex.haskell'] 61 | ] 62 | it "parses inline code with pipes", -> 63 | g = grammarExpect grammar, 'a |code||||| should parse correctly' 64 | g.toHaveTokens [['a ', '|', 'code', '||||', '|', ' should parse correctly']] 65 | g.toHaveScopes [['text.tex.latex.haskell']] 66 | g.tokenToHaveScopes [ 67 | 1: ['meta.embedded.text.haskell.latex.haskell'] 68 | 2: ["identifier.haskell"] 69 | 3: ["keyword.operator.haskell"] 70 | 4: ['meta.embedded.text.haskell.latex.haskell'] 71 | ] 72 | -------------------------------------------------------------------------------- /spec/operators-spec.coffee: -------------------------------------------------------------------------------- 1 | {grammarExpect, customMatchers} = require './util' 2 | prelude = require '../src/include/prelude' 3 | 4 | describe "Language-Haskell Operators", -> 5 | grammar = null 6 | 7 | beforeEach -> 8 | @addMatchers(customMatchers) 9 | waitsForPromise -> 10 | atom.packages.activatePackage("language-haskell") 11 | 12 | runs -> 13 | grammar = atom.grammars.grammarForScopeName("source.haskell") 14 | 15 | describe "operators", -> 16 | it "tokenizes the / arithmetic operator when separated by newlines", -> 17 | g = grammarExpect grammar, """ 18 | 1 19 | / 2 20 | """ 21 | g.toHaveTokens [ 22 | ['1'] 23 | ['/', ' ', '2'] 24 | ] 25 | g.toHaveScopes [['source.haskell'], ['source.haskell']] 26 | g.tokensToHaveScopes { 27 | '1': ['constant.numeric.decimal.haskell'] 28 | '/': ['keyword.operator.haskell'] 29 | '2': ['constant.numeric.decimal.haskell'] 30 | } 31 | prelude.operators.forEach (i) -> 32 | it "tokenizes #{i} operator", -> 33 | g = grammarExpect grammar, "a #{i} b" 34 | g.toHaveTokens [['a', ' ', i, ' ', 'b']] 35 | g.toHaveScopes [['source.haskell']] 36 | g.tokenToHaveScopes [2: ['keyword.operator.haskell', 'support.operator.prelude.haskell']] 37 | 38 | it "tokenizes (#{i}) operator function", -> 39 | g = grammarExpect grammar, "(#{i}) a b" 40 | g.toHaveTokens [["(#{i})", ' ', 'a', ' ', 'b']] 41 | g.toHaveScopes [['source.haskell']] 42 | g.tokenToHaveScopes [0: ['entity.name.function.operator.haskell', 'support.operator.prelude.haskell']] 43 | 44 | it "tokenizes qualified #{i} operator", -> 45 | g = grammarExpect grammar, "a Prelude.#{i} b" 46 | g.toHaveTokens [['a', ' ', 'Prelude.', i, ' ', 'b']] 47 | g.toHaveScopes [['source.haskell']] 48 | g.tokenToHaveScopes [ 49 | 2: ['keyword.operator.haskell', 'support.other.module.haskell'] 50 | 3: ['keyword.operator.haskell'] 51 | ] 52 | 53 | it "tokenizes qualified (#{i}) operator function", -> 54 | g = grammarExpect grammar, "(Prelude.#{i}) a b" 55 | g.toHaveTokens [['(', 'Prelude.', "#{i})", ' ', 'a', ' ', 'b']] 56 | g.toHaveScopes [['source.haskell']] 57 | g.tokenToHaveScopes [ 58 | 0: ['entity.name.function.operator.haskell'] 59 | 1: ['entity.name.function.operator.haskell', 'support.other.module.haskell'] 60 | 2: ['entity.name.function.operator.haskell'] 61 | ] 62 | -------------------------------------------------------------------------------- /spec/snippets-spec.coffee: -------------------------------------------------------------------------------- 1 | CSON = require 'season' 2 | 3 | defs = CSON.readFileSync("#{__dirname}/../snippets/language-haskell.cson") 4 | 5 | open = (what) -> 6 | atom.workspace.open("#{__dirname}/fixture/#{what}") 7 | 8 | xdescribe "Snippets", -> 9 | [editorElement, editor, Snippets] = [] 10 | 11 | expandSnippet = (editor) -> 12 | atom.commands.dispatch(atom.views.getView(editor), 'snippets:expand') 13 | 14 | sanitize = (body) -> 15 | parser = Snippets.getBodyParser() 16 | flatten = (obj) -> 17 | if typeof(obj) is "string" 18 | return obj 19 | else 20 | return obj.content.map(flatten).join('') 21 | parsed = 22 | parser.parse(body) 23 | .map(flatten) 24 | .join('') 25 | .replace /\t/g, ' '.repeat(editor.getTabLength()) 26 | return parsed 27 | 28 | universalTests = -> 29 | it 'triggers snippets', -> 30 | expect((for name, {prefix, body} of defs['.source .haskell'] 31 | editor.setText("") 32 | editor.insertText(prefix) 33 | expandSnippet(editor) 34 | expect(editor.getText().trim()).toBe sanitize(body).trim() 35 | ).length).toBeGreaterThan 0 36 | it 'triggers non-comment snippets', -> 37 | expect((for name, {prefix, body} of defs['.source .haskell:not(.comment)'] 38 | editor.setText("") 39 | editor.insertText(prefix) 40 | expandSnippet(editor) 41 | expect(editor.getText().trim()).toBe sanitize(body).trim() 42 | ).length).toBeGreaterThan 0 43 | it 'triggers comment snippets', -> 44 | expect((for name, {prefix, body} of defs['.source .haskell.comment'] 45 | editor.setText("") 46 | editor.insertText("-- #{prefix}") 47 | expandSnippet(editor) 48 | expect(editor.getText().trim()).toBe "-- #{sanitize(body).trim()}" 49 | ).length).toBeGreaterThan 0 50 | it 'triggers empty-list snippets', -> 51 | expect((for name, {prefix, body} of defs['.source .haskell.constant.language.empty-list'] 52 | editor.setText("") 53 | editor.insertText("#{prefix}]") 54 | editor.getLastCursor().moveLeft() 55 | expandSnippet(editor) 56 | expect(editor.getText().trim()).toBe "#{sanitize(body).trim()}]" 57 | ).length).toBeGreaterThan 0 58 | it 'triggers type snippets', -> 59 | expect((for name, {prefix, body} of defs['.source .haskell.meta.type'] 60 | editor.setText("") 61 | editor.insertText("data Data = Constr #{prefix}") 62 | expandSnippet(editor) 63 | expect(editor.getText().trim()).toBe "data Data = Constr #{sanitize(body).trim()}" 64 | ).length).toBeGreaterThan 0 65 | 66 | beforeEach -> 67 | waitsForPromise -> 68 | atom.packages.activatePackage("language-haskell") 69 | waitsForPromise -> 70 | snippets = atom.packages.loadPackage('snippets') 71 | snippets.activate() 72 | .then -> 73 | Snippets = snippets.mainModule 74 | new Promise (resolve) -> 75 | Snippets.onDidLoadSnippets -> resolve() 76 | 77 | describe 'haskell', -> 78 | beforeEach -> 79 | waitsForPromise -> 80 | open('sample.hs') 81 | runs -> 82 | editor = atom.workspace.getActiveTextEditor() 83 | editorElement = atom.views.getView(editor) 84 | universalTests() 85 | describe 'c2hs', -> 86 | beforeEach -> 87 | waitsForPromise -> 88 | open('sample.chs') 89 | runs -> 90 | editor = atom.workspace.getActiveTextEditor() 91 | editorElement = atom.views.getView(editor) 92 | universalTests() 93 | describe 'hsc2hs', -> 94 | beforeEach -> 95 | waitsForPromise -> 96 | open('sample.hsc') 97 | runs -> 98 | editor = atom.workspace.getActiveTextEditor() 99 | editorElement = atom.views.getView(editor) 100 | universalTests() 101 | 102 | describe 'cabal', -> 103 | beforeEach -> 104 | waitsForPromise -> 105 | open('sample.cabal') 106 | runs -> 107 | editor = atom.workspace.getActiveTextEditor() 108 | editorElement = atom.views.getView(editor) 109 | it 'triggers snippets', -> 110 | expect((for name, {prefix, body} of defs['.source.cabal'] 111 | editor.setText("") 112 | editor.insertText(prefix) 113 | expandSnippet(editor) 114 | expect(editor.getText().trim()).toBe sanitize(body).trim() 115 | ).length).toBeGreaterThan 0 116 | -------------------------------------------------------------------------------- /spec/util.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore-plus' 2 | 3 | zip = -> 4 | lengthArray = (arr.length for arr in arguments) 5 | length = Math.max(lengthArray...) 6 | for i in [0...length] 7 | arr[i] for arr in arguments 8 | 9 | module.exports = 10 | grammarExpect: (grammar, str) -> 11 | tkzd = grammar.tokenizeLines(str) 12 | expect(tkzd) 13 | 14 | customMatchers: 15 | toHaveTokens: (expected) -> 16 | for [a, e] in zip(@actual, expected) 17 | ts = a.map ({value}) -> value 18 | unless (_.isEqual(ts, e)) 19 | # console.log @message 20 | @message = -> "Expected #{JSON.stringify(ts)} to equal #{JSON.stringify(e)}" 21 | return false 22 | return true 23 | tokensToHaveScopes: (expected) -> 24 | for line in @actual 25 | for tok in line 26 | if tok.value of expected 27 | for sc in expected[tok.value] 28 | if not _.contains(tok.scopes, sc) 29 | @message = -> """ 30 | Expected #{JSON.stringify(tok)} to 31 | have scope #{sc} from #{JSON.stringify(expected[tok.value])} 32 | """ 33 | return false 34 | return true 35 | tokensNotToHaveScopes: (expected) -> 36 | for line in @actual 37 | for tok in line 38 | if tok.value of expected 39 | for sc in expected[tok.value] 40 | if _.contains(tok.scopes, sc) 41 | @message = -> """ 42 | Expected #{JSON.stringify(tok)} to not 43 | have scope #{sc} from #{JSON.stringify(expected[tok.value])} 44 | """ 45 | return false 46 | return true 47 | toHaveScopes: (expected) -> 48 | zip(@actual, expected).every ([a, e]) -> 49 | a.every ({scopes}) -> 50 | e.every (s) -> _.contains(scopes, s) 51 | notToHaveScopes: (expected) -> 52 | not zip(@actual, expected).every ([a, e]) -> 53 | a.every ({scopes}) -> 54 | e.every (s) -> _.contains(scopes, s) 55 | tokenToHaveScopes: (expected) -> 56 | for [a, e] in zip(@actual, expected) 57 | for i, s of e 58 | if not Array.isArray(s) or s.length is 0 59 | @message = -> "Zero-length assertion in #{i} of #{JSON.stringify(e)}" 60 | return false 61 | for sc in s 62 | unless _.contains(a[i].scopes, sc) 63 | @message = -> "Expected token #{i} #{JSON.stringify(a[i])} to have scope #{sc} from #{JSON.stringify(s)}" 64 | return false 65 | return true 66 | tokenNotToHaveScopes: (expected) -> 67 | for [a, e] in zip(@actual, expected) 68 | for i, s of e 69 | if not Array.isArray(s) or s.length is 0 70 | @message = -> "Zero-length assertion in #{i} of #{JSON.stringify(e)}" 71 | return false 72 | for sc in s 73 | if _.contains(a[parseInt(i, 10)].scopes, sc) 74 | @message = -> """ 75 | Expected token #{i} #{JSON.stringify(a[i])} not to have scope #{sc} from #{JSON.stringify(s)} 76 | """ 77 | return false 78 | return true 79 | -------------------------------------------------------------------------------- /src/haskell.coffee: -------------------------------------------------------------------------------- 1 | {include, makeGrammar} = require './syntax-tools' 2 | _ = require 'underscore-plus' 3 | 4 | makeGrammar "grammars/haskell.cson", 5 | name: 'Haskell' 6 | fileTypes: [ 'hs', 'hs-boot', 'cpphs' ] 7 | firstLineMatch: '^\\#\\!.*\\brunhaskell\\b' 8 | scopeName: 'source.haskell' 9 | 10 | macros: include 'macros' 11 | repository: include 'repository' 12 | patterns: include 'haskell-patterns' 13 | 14 | makeGrammar "grammars/module signature.cson", 15 | name: 'Haskell Module Signature' 16 | fileTypes: [ 'hsig' ] 17 | scopeName: 'source.hsig' 18 | 19 | macros: include('macros') 20 | repository: include 'repository' 21 | patterns: include 'hsig-patterns' 22 | 23 | makeGrammar "grammars/haskell autocompletion hint.cson", 24 | # name: 'Haskell Autocompletion Hint' 25 | fileTypes: [] 26 | scopeName: 'hint.haskell' 27 | 28 | macros: include 'macros' 29 | patterns: [ 30 | {include: '#function_type_declaration'} 31 | {include: '#ctor_type_declaration'} 32 | ] 33 | repository: include 'repository' 34 | 35 | makeGrammar "grammars/haskell type hint.cson", 36 | # name: 'Haskell Type Hint' 37 | fileTypes: [] 38 | scopeName: 'hint.type.haskell' 39 | 40 | macros: include 'macros' 41 | patterns: [ 42 | include: '#type_signature' 43 | ] 44 | repository: include 'repository' 45 | 46 | makeGrammar "grammars/haskell message hint.cson", 47 | # name: 'Haskell Message Hint' 48 | fileTypes: [] 49 | scopeName: 'hint.message.haskell' 50 | 51 | macros: include 'macros' 52 | patterns: [ 53 | match: /^[^:]*:(.+)$/ 54 | captures: 55 | 1: 56 | patterns: [ 57 | include: 'source.haskell' 58 | ] 59 | , 60 | begin: /^[^:]*:$/ 61 | end: /^(?=\S)/ 62 | patterns: [ 63 | include: 'source.haskell' 64 | ] 65 | , 66 | begin: /‘/ 67 | end: /’/ 68 | patterns: [ 69 | include: 'source.haskell' 70 | ] 71 | ] 72 | repository: include 'repository' 73 | 74 | makeGrammar "grammars/literate haskell.cson", 75 | name: 'Literate Haskell' 76 | fileTypes: [ 'lhs' ] 77 | scopeName: 'text.tex.latex.haskell' 78 | 79 | macros: _.extend (require 'clone')(include('macros')), 80 | maybeBirdTrack: /^(?:>|<) / 81 | indentBlockEnd: 82 | /^(?!(?:>|<) \1{indentChar}|(?:>|<) {indentChar}*$)|^(?!(?:>|<) )/ 83 | indentBlockCont: 84 | /^(?!(?:>|<) \1|(?:>|<) {indentChar}*$)|^(?!(?:>|<) )/ 85 | operatorChar: '(?:[\\p{S}\\p{P}](?<] )/ 30 | end: /^(?![><] )/ 31 | name: 'meta.embedded.haskell' 32 | patterns: (require './haskell-patterns').concat 33 | match: /^> / 34 | name: 'punctuation.definition.bird-track.haskell' 35 | , 36 | match: '(?|=>)+\s*)+ #anything goes! 49 | ) 50 | /// 51 | ctor: concat /{lb}({className})\s*/, listMaybe(/{ctorArgs}/, /\s+/) 52 | typeDeclOne: /(?:(?!{lb}where{rb})(?:{className}|{functionName}))/ 53 | typeDecl: '(?>(?:{typeDeclOne})(?:\\s+{typeDeclOne})*)' 54 | indentChar: /[ \t]/ 55 | indentBlockStart: '{maybeBirdTrack}({indentChar}*)' 56 | indentBlockEnd: /^(?!\1{indentChar}|{indentChar}*$)/ 57 | indentBlockCont: /^(?!\1|{indentChar}*$)/ 58 | maybeBirdTrack: /^/ 59 | lb: '(?:(?={identStartCharClass})(?|→' 67 | big_arrow: guarded '=>|⇒' 68 | type_ctor_alt_delim: /^(?!{maybeBirdTrack}{indentChar}|{indentChar}*$)|(?=\{|\}|\||{lb}deriving{rb})/ 69 | -------------------------------------------------------------------------------- /src/include/pragmas.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore-plus' 2 | 3 | # Taken from https://github.com/ghc/ghc/blob/master/compiler/parser/Lexer.x#L2673 4 | pragmas = [ 5 | # Line pragma 6 | 'LINE' 7 | 'COLUMN' 8 | # Header pragmas 9 | 'OPTIONS' 10 | 'OPTIONS_GHC' 11 | 'OPTIONS_HADDOCK' 12 | 'LANGUAGE' 13 | 'INCLUDE' 14 | # Ignored pragmas 15 | 'OPTIONS_HUGS' 16 | 'OPTIONS_NHC98' 17 | 'OPTIONS_JHC' 18 | 'OPTIONS_YHC' 19 | 'OPTIONS_CATCH' 20 | 'OPTIONS_DERIVE' 21 | 'CFILES' 22 | 'CONTRACT' 23 | # One-word pragmas 24 | 'RULES' 25 | 'INLINE' 26 | 'INLINABLE' 27 | 'INLINEABLE' 28 | 'NOTINLINE' 29 | 'SPECIALIZE' 30 | 'SOURCE' 31 | 'WARNING' 32 | 'DEPRECATED' 33 | 'SCC' 34 | 'GENERATED' 35 | 'CORE' 36 | 'UNPACK' 37 | 'NOUNPACK' 38 | 'ANN' 39 | 'VECTORIZE' 40 | 'NOVECTORIZE' 41 | 'MINIMAL' 42 | 'OVERLAPS' 43 | 'OVERLAPPABLE' 44 | 'OVERLAPPING' 45 | 'INCOHERENT' 46 | 'CTYPE' 47 | 'COMPLETE' 48 | # Two word pragmas 49 | 'INLINE CONLIKE' 50 | 'NOTINLINE CONLIKE' 51 | 'SPECIALIZE INLINE' 52 | 'SPECIALIZE NOTINLINE' 53 | 'VECTORIZE SCALAR' 54 | ] 55 | 56 | # Spelling variants 57 | spell_var = _.invert { 58 | 'NOINLINE': 'NOTINLINE' 59 | 'SPECIALISE': 'SPECIALIZE' 60 | 'VECTORISE': 'VECTORIZE' 61 | 'NOVECTORISE': 'NOVECTORIZE' 62 | 'CONSTRUCTORLIKE': 'CONLIKE' 63 | } 64 | 65 | variants = (p) -> 66 | len = p.split(' ').length 67 | mask0 = Math.pow(2, len) - 1 68 | for mask in [0..mask0] 69 | ws = p.split(' ') 70 | for w, i in ws 71 | if mask & Math.pow(2, i) 72 | ws[i] = spell_var[w] ? w 73 | ws.join(' ') 74 | 75 | for p in pragmas 76 | pragmas.push variants(p)... 77 | 78 | sortf = (a, b) -> 79 | d = b.length - a.length 80 | if d isnt 0 81 | d 82 | else 83 | a.localeCompare b 84 | 85 | module.exports = _.uniq pragmas.sort sortf 86 | -------------------------------------------------------------------------------- /src/include/prelude.coffee: -------------------------------------------------------------------------------- 1 | # coffeelint: disable 2 | classes = ["Applicative","Bounded","Enum","Eq","Floating","Foldable","Fractional","Functor","Integral","Monad","Monoid","Num","Ord","Read","Real","RealFloat","RealFrac","Show","Traversable"] 3 | funct = ["abs","acos","acosh","all","and","any","appendFile","asTypeOf","asin","asinh","atan","atan2","atanh","break","ceiling","compare","concat","concatMap","const","cos","cosh","curry","cycle","decodeFloat","div","divMod","drop","dropWhile","either","elem","encodeFloat","enumFrom","enumFromThen","enumFromThenTo","enumFromTo","error","errorWithoutStackTrace","even","exp","exponent","fail","filter","flip","floatDigits","floatRadix","floatRange","floor","fmap","foldMap","foldl","foldl1","foldr","foldr1","fromEnum","fromInteger","fromIntegral","fromRational","fst","gcd","getChar","getContents","getLine","head","id","init","interact","ioError","isDenormalized","isIEEE","isInfinite","isNaN","isNegativeZero","iterate","last","lcm","length","lex","lines","log","logBase","lookup","map","mapM","mapM_","mappend","max","maxBound","maximum","maybe","mconcat","mempty","min","minBound","minimum","mod","negate","not","notElem","null","odd","or","otherwise","pi","pred","print","product","properFraction","pure","putChar","putStr","putStrLn","quot","quotRem","read","readFile","readIO","readList","readLn","readParen","reads","readsPrec","realToFrac","recip","rem","repeat","replicate","return","reverse","round","scaleFloat","scanl","scanl1","scanr","scanr1","seq","sequence","sequenceA","sequence_","show","showChar","showList","showParen","showString","shows","showsPrec","significand","signum","sin","sinh","snd","span","splitAt","sqrt","subtract","succ","sum","tail","take","takeWhile","tan","tanh","toEnum","toInteger","toRational","traverse","truncate","uncurry","undefined","unlines","until","unwords","unzip","unzip3","userError","words","writeFile","zip","zip3","zipWith","zipWith3"] 4 | constr = ["EQ","GT","LT","Left","Right","True","False"] 5 | types = ["Either","FilePath","IO","IOError","Integer","Ordering","Rational","ReadS","ShowS","String","Bool","Char","Double","Float","Int","Just","Maybe","Nothing","Word"] 6 | operators = ["!!","$!","$","&&","*","**","*>","+","++","-",".","/","/=","<$","<$>","<","<*","<*>","<=","=<<","==",">",">=",">>",">>=","^","^^","||"] 7 | module.exports = { classes,funct,constr,types,operators } 8 | -------------------------------------------------------------------------------- /src/include/repository.coffee: -------------------------------------------------------------------------------- 1 | prelude = require './prelude' 2 | pragmas = require './pragmas' 3 | { balanced, guarded, floatPattern, controlKeywords, otherKeywords } = require './util' 4 | 5 | module.exports= 6 | block_comment: 7 | patterns: [ 8 | name: 'comment.block.haddock.haskell' 9 | begin: /\{-\s*[|^]/ 10 | end: /-\}/ 11 | applyEndPatternLast: 1 12 | beginCaptures: 13 | 0: name: 'punctuation.definition.comment.haddock.haskell' 14 | endCaptures: 15 | 0: name: 'punctuation.definition.comment.haddock.haskell' 16 | patterns: [ 17 | include: '#block_comment' 18 | ] 19 | , 20 | name: 'comment.block.haskell' 21 | begin: /\{-/ 22 | end: /-\}/ 23 | applyEndPatternLast: 1 24 | beginCaptures: 25 | 0: name: 'punctuation.definition.comment.block.start.haskell' 26 | endCaptures: 27 | 0: name: 'punctuation.definition.comment.block.end.haskell' 28 | patterns: [ 29 | include: '#block_comment' 30 | ] 31 | ] 32 | comments: 33 | patterns: [ 34 | begin: /({maybeBirdTrack}[ \t]+)?(?=--+\s+[|^])/ 35 | end: /(?!\G)/ 36 | patterns: [ 37 | begin: /(--+)\s+([|^])(.*)/ 38 | end: /^(?!--+)/ 39 | beginCaptures: 40 | 1: name: 'punctuation.definition.comment.haskell' 41 | 2: name: 'punctuation.definition.comment.haddock.haskell' 42 | 3: name: 'comment.line.double-dash.haddock.haskell' 43 | patterns: 44 | match: /^(--+)(.*)/ 45 | captures: 46 | 1: name: 'punctuation.definition.comment.haskell' 47 | 2: name: 'comment.line.double-dash.haddock.haskell' 48 | ] 49 | , 50 | ### 51 | Operators may begin with -- as long as they are not 52 | entirely composed of - characters. This means comments can't be 53 | immediately followed by an allowable operator character. 54 | ### 55 | begin: /({maybeBirdTrack}[ \t]+)?(?=--+(?!{operatorChar}))/ 56 | end: /(?!\G)/ 57 | patterns: [ 58 | name: 'comment.line.double-dash.haskell' 59 | begin: /--+/ 60 | end: /$/ 61 | beginCaptures: 62 | 0: name: 'punctuation.definition.comment.haskell' 63 | ] 64 | , 65 | include: '#block_comment' 66 | ] 67 | characters: 68 | patterns: [ 69 | {match: '{escapeChar}', name: 'constant.character.escape.haskell'} 70 | {match: '{octalChar}', name: 'constant.character.escape.octal.haskell'} 71 | {match: '{hexChar}', name: 'constant.character.escape.hexadecimal.haskell'} 72 | {match: '{controlChar}', name: 'constant.character.escape.control.haskell'} 73 | ] 74 | module_exports: 75 | name: 'meta.declaration.exports.haskell' 76 | begin: /\(/ 77 | end: /\)/ 78 | applyEndPatternLast: 1 79 | patterns: [ 80 | include: '#comments' 81 | , 82 | include: '#c_preprocessor' 83 | , 84 | begin: /{lb}(module){rb}/ 85 | end: /{lb}({className}){rb}/ 86 | beginCaptures: 87 | 1: name: 'keyword.other.haskell' 88 | endCaptures: 89 | 1: name: 'support.other.module.haskell' 90 | patterns: [ 91 | {include: '#invalid'} 92 | ] 93 | , 94 | include: '#pattern_name' 95 | , 96 | include: '#type_exportImport' 97 | , 98 | include: '#function_name' 99 | , 100 | include: '#type_name' 101 | , 102 | include: '#comma' 103 | , 104 | include: '#infix_op' 105 | , 106 | name: 'meta.other.constructor-list.haskell' 107 | begin: /\(/ 108 | end: /\)/ 109 | patterns: [ 110 | { include: '#comments' } 111 | { include: '#c_preprocessor' } 112 | { include: '#type_ctor' } 113 | { include: '#attribute_name' } 114 | { include: '#comma' } 115 | { 116 | match: /\.\./ 117 | name: 'keyword.operator.wildcard.haskell' 118 | } 119 | {include: '#infix_op'} 120 | ] 121 | ] 122 | module_name: 123 | name: 'support.other.module.haskell' 124 | match: /{lb}{className}{rb}/ 125 | module_name_prefix: 126 | name: 'support.other.module.haskell' 127 | match: /{lb}{className}\./ 128 | pragma: 129 | name: 'meta.preprocessor.haskell' 130 | begin: /\{-#/ 131 | end: /#-\}/ 132 | patterns: [ 133 | match: "{lb}((?i:#{pragmas.join('|')})){rb}" 134 | name: 'keyword.other.preprocessor.$1.haskell' 135 | ] 136 | function_type_declaration: 137 | name: 'meta.function.type-declaration.haskell' 138 | begin: /{indentBlockStart}{functionTypeDeclaration}/ 139 | end: '{indentBlockEnd}|(?={scoped_assignment})' 140 | contentName: 'meta.type-signature.haskell' 141 | beginCaptures: 142 | 2: 143 | patterns: [ 144 | {include: '#function_name'} 145 | {include: '#infix_op'} 146 | ] 147 | 3: name: 'keyword.other.double-colon.haskell' 148 | patterns: [ 149 | include: '#type_signature' 150 | ] 151 | multiline_type_declaration: 152 | name: 'meta.multiline.type-declaration.haskell' 153 | begin: /{indentBlockStart}({doubleColonOperator})/ 154 | end: '{indentBlockCont}|(?={scoped_assignment})' 155 | contentName: 'meta.type-signature.haskell' 156 | beginCaptures: 157 | 2: name: 'keyword.other.double-colon.haskell' 158 | patterns: [ 159 | {include: '#type_signature'} 160 | ] 161 | lazy_function_type_signature: 162 | name: 'meta.function.type-declaration.haskell' 163 | begin: /{indentBlockStart}({functionList})\s*$/ 164 | end: /{indentBlockEnd}/ 165 | contentName: 'meta.type-signature.haskell' 166 | beginCaptures: 167 | 2: 168 | patterns: [ 169 | {include: '#function_name'} 170 | {include: '#infix_op'} 171 | ] 172 | patterns: [ 173 | {include: '#double_colon_operator'} 174 | {include: '#type_signature'} 175 | ] 176 | double_colon_operator: 177 | name: 'keyword.other.double-colon.haskell' 178 | match: '{doubleColonOperator}' 179 | ctor_type_declaration: 180 | name: 'meta.ctor.type-declaration.haskell' 181 | begin: /{indentBlockStart}{ctorTypeDeclaration}/ 182 | end: /{indentBlockEnd}/ 183 | contentName: 'meta.type-signature.haskell' 184 | beginCaptures: 185 | 2: 186 | patterns: [ 187 | include: '#type_ctor' 188 | , 189 | include: '#infix_op' 190 | ] 191 | 3: name: 'keyword.other.double-colon.haskell' 192 | patterns: [ 193 | include: '#type_signature' 194 | ] 195 | record_field_declaration: 196 | name: 'meta.record-field.type-declaration.haskell' 197 | begin: /{lb}{functionTypeDeclaration}/ 198 | end: /(?={functionTypeDeclaration}|})/ 199 | contentName: 'meta.type-signature.haskell' 200 | beginCaptures: 201 | 1: 202 | patterns: [ 203 | include: '#attribute_name' 204 | , 205 | include: '#infix_op' 206 | ] 207 | 2: name: 'keyword.other.double-colon.haskell' 208 | patterns: [ 209 | include: '#type_signature' 210 | ] 211 | type_signature: 212 | patterns: [ 213 | #TODO: Type operators, type-level integers etc 214 | include: '#pragma' 215 | , 216 | include: '#comments' 217 | , 218 | name: 'keyword.other.forall.haskell' 219 | match: '{lb}forall{rb}' 220 | , 221 | include: '#quoted_character' 222 | , 223 | match: /'(\(\))/ 224 | name: 'other.promoted.haskell' 225 | captures: 1: patterns: [ 226 | {include: '#unit'} 227 | ] 228 | , 229 | include: '#unit' 230 | , 231 | match: /'(\[\])/ 232 | name: 'other.promoted.haskell' 233 | captures: 1: patterns: [ 234 | {include: '#empty_list'} 235 | ] 236 | , 237 | include: '#empty_list' 238 | , 239 | include: '#string' 240 | , 241 | include: '#arrow' 242 | , 243 | include: '#big_arrow' 244 | , 245 | match: "'({operator})" 246 | name: 'other.promoted.haskell' 247 | captures: 1: patterns: [ 248 | {include: '#operator'} 249 | ] 250 | , 251 | include: '#operator' 252 | , 253 | include: '#type_variable' 254 | , 255 | name: 'other.promoted.haskell' 256 | match: /{lbrel}'({className}){rb}/ 257 | captures: 1: patterns: [ 258 | include: '#type_name' 259 | ] 260 | , 261 | include: '#type_name' 262 | , 263 | include: '#lit_num' 264 | ] 265 | arrow: 266 | name: 'keyword.other.arrow.haskell' 267 | match: '{arrow}' 268 | big_arrow: 269 | name: 'keyword.other.big-arrow.haskell' 270 | match: '{big_arrow}' 271 | type_variable: 272 | name: 'variable.other.generic-type.haskell' 273 | match: /{lb}{functionName}{rb}/ 274 | unit: 275 | name: 'constant.language.unit.haskell' 276 | match: /\(\)/ 277 | empty_list: 278 | name: 'constant.language.empty-list.haskell' 279 | match: /\[\]/ 280 | deriving: 281 | patterns: [ 282 | {include: '#deriving_list'} 283 | {include: '#deriving_simple'} 284 | {include: '#deriving_keyword'} 285 | ] 286 | deriving_strategies: 287 | name: 'meta.deriving.strategy.haskell' 288 | match: '{lb}(stock|newtype|anyclass){rb}' 289 | captures: 290 | 1: name: 'keyword.other.haskell' 291 | deriving_keyword: 292 | name: 'meta.deriving.haskell' 293 | match: /{lb}{deriving}{rb}/ 294 | captures: 295 | 1: name: 'keyword.other.haskell' 296 | 2: patterns: [{include: '#deriving_strategies'}] 297 | deriving_list: 298 | name: 'meta.deriving.haskell' 299 | begin: /{lb}{deriving}\s*\(/ 300 | end: /\)/ 301 | beginCaptures: 302 | 1: name: 'keyword.other.haskell' 303 | 2: patterns: [{include: '#deriving_strategies'}] 304 | patterns: [ 305 | match: /{lb}({className}){rb}/ 306 | captures: 307 | 1: name: 'entity.other.inherited-class.haskell' 308 | ] 309 | deriving_simple: 310 | name: 'meta.deriving.haskell' 311 | match: /{lb}{deriving}\s*({className}){rb}/ 312 | captures: 313 | 1: name: 'keyword.other.haskell' 314 | 2: patterns: [{include: '#deriving_strategies'}] 315 | 3: name: 'entity.other.inherited-class.haskell' 316 | via: 317 | patterns: [ 318 | {include: '#via_list'} 319 | {include: '#via_list_newline'} 320 | {include: '#via_indent'} 321 | {include: '#via_simple'} 322 | {include: '#via_keyword'} 323 | ] 324 | via_keyword: 325 | name: 'meta.via.haskell' 326 | match: /{lb}(via){rb}/ 327 | captures: 328 | 1: name: 'keyword.other.haskell' 329 | via_simple: 330 | name: 'meta.via.haskell' 331 | match: /{lb}(via)\s*({className}){rb}/ 332 | captures: 333 | 1: name: 'keyword.other.haskell' 334 | 2: patterns: [include: "#type_signature"] 335 | via_list: 336 | name: 'meta.via.haskell' 337 | begin: /{lb}(via)\s*\(/ 338 | end: /\)/ 339 | beginCaptures: 340 | 1: name: 'keyword.other.haskell' 341 | patterns: [ 342 | {include: "#type_signature"} 343 | ] 344 | via_list_newline: 345 | name: 'meta.via.haskell' 346 | begin: /{lb}(via)\s*/ 347 | end: /$/ 348 | beginCaptures: 349 | 1: name: 'keyword.other.haskell' 350 | patterns: [ 351 | {include: "#type_signature"} 352 | ] 353 | via_indent: 354 | name: 'meta.via.haskell' 355 | begin: /{indentBlockStart}(via)\s*/ 356 | end: /{indentBlockCont}/ 357 | beginCaptures: 358 | 2: name: 'keyword.other.haskell' 359 | patterns: [ 360 | {include: "#type_signature"} 361 | ] 362 | infix_function: 363 | name: 'keyword.operator.function.infix.haskell' 364 | match: /(`){functionName}(`)/ 365 | captures: 366 | 1: name: 'punctuation.definition.entity.haskell' 367 | 2: name: 'punctuation.definition.entity.haskell' 368 | quasi_quotes: 369 | begin: /(\[)((?:{className}\.)?({functionNameOne}))(\|)/ 370 | end: /(\|)(\])/ 371 | beginCaptures: 372 | 1: name: 'punctuation.definition.quasiquotes.begin.haskell' 373 | 2: 374 | name: 'entity.name.tag.haskell' 375 | patterns: { include: '#module_name_prefix' } 376 | endCaptures: 377 | 2: name: 'punctuation.definition.quasiquotes.end.haskell' 378 | contentName: 'quoted.quasiquotes.qq-$3.haskell' 379 | module_decl: 380 | name: 'meta.declaration.module.haskell' 381 | begin: /{indentBlockStart}(module){rb}/ 382 | end: /{lb}(where){rb}|{indentBlockEnd}/ 383 | beginCaptures: 384 | 2: name: 'keyword.other.haskell' 385 | endCaptures: 386 | 1: name: 'keyword.other.haskell' 387 | patterns: [ 388 | {include: '#pragma'} 389 | {include: '#comments'} 390 | {include: '#module_name'} 391 | {include: '#module_exports'} 392 | {include: '#invalid'} 393 | ] 394 | hsig_decl: 395 | name: 'meta.declaration.module.haskell' 396 | begin: /{indentBlockStart}(signature){rb}/ 397 | end: /{lb}(where){rb}|{indentBlockEnd}/ 398 | beginCaptures: 399 | 2: name: 'keyword.other.haskell' 400 | endCaptures: 401 | 1: name: 'keyword.other.haskell' 402 | patterns: [ 403 | {include: '#comments'} 404 | {include: '#module_name'} 405 | {include: '#module_exports'} 406 | {include: '#invalid'} 407 | ] 408 | class_decl: 409 | name: 'meta.declaration.class.haskell' 410 | begin: /{indentBlockStart}(class){rb}/ 411 | end: /{lb}(where){rb}|{indentBlockEnd}/ 412 | beginCaptures: 413 | 2: name: 'keyword.other.class.haskell' 414 | endCaptures: 415 | 1: name: 'keyword.other.haskell' 416 | patterns: [ 417 | include: '#type_signature' 418 | ] 419 | instance_decl: 420 | name: 'meta.declaration.instance.haskell' 421 | begin: /{indentBlockStart}(instance){rb}/ 422 | end: /{lb}(where){rb}|{indentBlockEnd}/ 423 | contentName: 'meta.type-signature.haskell' 424 | beginCaptures: 425 | 2: name: 'keyword.other.haskell' 426 | endCaptures: 427 | 1: name: 'keyword.other.haskell' 428 | patterns: [ 429 | {include: '#pragma'} 430 | {include: '#type_signature'} 431 | ] 432 | deriving_instance_decl: 433 | name: 'meta.declaration.instance.deriving.haskell' 434 | begin: /{indentBlockStart}(?:{deriving}\s+|(deriving)\s+(via)\s+(.*)\s+)?(instance){rb}/ 435 | end: /{indentBlockEnd}/ 436 | contentName: 'meta.type-signature.haskell' 437 | beginCaptures: 438 | 2: name: 'keyword.other.haskell' # deriving 439 | 3: patterns: [{include: '#deriving_strategies'}] # stragegy 440 | 4: name: 'keyword.other.haskell' # deriving 441 | 5: name: 'keyword.other.haskell' # via 442 | 6: { # sig 443 | name: 'meta.type-signature.haskell' 444 | patterns: [{include: '#type_signature'}] 445 | } 446 | 7: name: 'keyword.other.haskell' #instance 447 | patterns: [ 448 | {include: '#pragma'} 449 | {include: '#type_signature'} 450 | ] 451 | foreign_import: 452 | name: 'meta.foreign.haskell' 453 | begin: /{indentBlockStart}(foreign)\s+(import|export){rb}/ 454 | end: /{indentBlockEnd}/ 455 | beginCaptures: 456 | 2: name: 'keyword.other.haskell' 457 | 3: name: 'keyword.other.haskell' 458 | patterns:[ 459 | match: /(?:un)?safe/ 460 | captures: 461 | 0: name: 'keyword.other.haskell' 462 | , 463 | include: '#function_type_declaration' 464 | , 465 | include: '#haskell_expr' 466 | , 467 | include: '#comments' 468 | ] 469 | regular_import: 470 | name: 'meta.import.haskell' 471 | begin: /{indentBlockStart}(import){rb}/ 472 | end: /{indentBlockEnd}/ 473 | beginCaptures: 474 | 2: name: 'keyword.other.haskell' 475 | patterns: [ 476 | include: '#module_name' 477 | , 478 | include: '#module_exports' 479 | , 480 | match: /{lb}(qualified|as|hiding){rb}/ 481 | captures: 482 | 1: name: 'keyword.other.haskell' 483 | , 484 | include: '#comments' 485 | ] 486 | data_decl: 487 | name: 'meta.declaration.type.data.haskell' 488 | begin: /{indentBlockStart}(data|newtype)\s+{data_def}/ 489 | end: /{indentBlockEnd}/ 490 | beginCaptures: 491 | 2: name: 'keyword.other.data.haskell' 492 | 3: 493 | name: 'meta.type-signature.haskell' 494 | patterns: [ 495 | {include: '#family_and_instance'} 496 | {include: '#type_signature'} 497 | ] 498 | patterns: [ 499 | {include: '#comments'} 500 | {include: '#string'} 501 | {include: '#where'} 502 | {include: '#deriving'} 503 | {include: '#via'} 504 | {include: '#assignment_op'} 505 | {include: '#type_ctor_forall'} 506 | {include: '#type_ctor_alt'} 507 | { 508 | match: /\|/ 509 | captures: 510 | 0: name: 'punctuation.separator.pipe.haskell' 511 | } 512 | { 513 | name: 'meta.declaration.type.data.record.block.haskell' 514 | begin: /\{/ 515 | beginCaptures: 516 | 0: name: 'keyword.operator.record.begin.haskell' 517 | end: /\}/ 518 | endCaptures: 519 | 0: name: 'keyword.operator.record.end.haskell' 520 | patterns: [ 521 | {include: '#comments'} 522 | {include: '#comma'} 523 | {include: '#record_field_declaration'} 524 | ] 525 | } 526 | {include: '#ctor_type_declaration'} #GADT 527 | ] 528 | type_ctor_forall: 529 | begin: '{lb}forall{rb}' 530 | end: '{type_ctor_alt_delim}' 531 | contentName: 'meta.type-signature' 532 | beginCaptures: 533 | 0: patterns: [include: '#type_signature'] 534 | patterns: [ 535 | {include: '#comments'} 536 | { 537 | match: '\\G.*?{big_arrow}' 538 | captures: 0: patterns: [include: '#type_signature'] 539 | } 540 | { 541 | match: '\\G.*?\\.' 542 | captures: 0: patterns: [include: '#type_signature'] 543 | } 544 | { include: '#big_arrow' } 545 | { include: '#type_variable' } 546 | { 547 | begin: '\\(' 548 | end: '\\)' 549 | patterns: [include: '#type_signature'] 550 | } 551 | {include: '#type_ctor_alt'} 552 | ] 553 | type_ctor_alt: 554 | begin: '{lb}({className})\\s*' 555 | end: '{type_ctor_alt_delim}' 556 | contentName: 'meta.type-signature' 557 | beginCaptures: 558 | 1: patterns: [include: '#type_ctor'] 559 | patterns: [ 560 | {include: '#comments'} 561 | {include: '#type_signature'} 562 | ] 563 | type_alias: 564 | name: 'meta.declaration.type.type.haskell' 565 | begin: /{indentBlockStart}(type){rb}/ 566 | end: /{indentBlockEnd}/ 567 | contentName: 'meta.type-signature.haskell' 568 | beginCaptures: 569 | 2: name: 'keyword.other.type.haskell' 570 | patterns: [ 571 | {include: '#comments'} 572 | {include: '#family_and_instance'} 573 | {include: '#where'} 574 | {include: '#assignment_op'} 575 | {include: '#type_signature'} 576 | ] 577 | keywords: [ 578 | name: 'keyword.other.$1.haskell' 579 | match: "{lb}(#{otherKeywords.join('|')}){rb}" 580 | , 581 | name: 'keyword.operator.$1.haskell' 582 | match: /{lb}(infix[lr]?){rb}/ 583 | , 584 | name: 'keyword.control.$1.haskell' 585 | match: "{lb}(#{controlKeywords.join('|')}){rb}" 586 | ] 587 | c_preprocessor: 588 | name: 'meta.preprocessor.c' 589 | begin: /{maybeBirdTrack}(?=#)/ 590 | end: '(? x.replace(/./g, (y) -> '\\'+y)).join('|')})$" 687 | } 688 | ] 689 | infix_op: 690 | name: 'entity.name.function.operator.haskell' 691 | match: /{operatorFun}/ 692 | captures: 693 | 0: patterns: [ 694 | { include: '#module_name_prefix' } 695 | { 696 | name: 'support.operator.prelude.haskell' 697 | match: "^\\((#{prelude.operators.map((x) -> x.replace(/./g, (y) -> '\\'+y)).join('|')})\\)$" 698 | } 699 | ] 700 | identifier: 701 | match: '{lb}{functionName}{rb}' 702 | name: 'identifier.haskell' 703 | captures: 0: patterns: [ 704 | { include: '#module_name_prefix' } 705 | { 706 | name: 'support.function.prelude.$1.haskell' 707 | match: "{lb}(#{prelude.funct.join('|')}){rb}" 708 | } 709 | ] 710 | pattern_name: 711 | name: 'meta.declaration.export.qualified.pattern.haskell' 712 | match: /{lb}(pattern)\s+({className}{rb}|{operatorFun})/ 713 | captures: 714 | 1: patterns: [ '#keywords' ] 715 | 2: patterns: [ '#type_ctor', '#infix_op' ] 716 | type_exportImport: 717 | name: 'meta.declaration.export.qualified.type.haskell' 718 | match: /{lb}(type)\s+({className}{rb}|{operatorFun})/ 719 | captures: 720 | 1: patterns: [ '#keywords' ] 721 | 2: patterns: [ '#type_name', '#operator' ] 722 | type_name: 723 | name: 'entity.name.type.haskell' 724 | match: /{lb}{className}{rb}/ 725 | captures: 0: patterns: [ 726 | { include: '#module_name_prefix' } 727 | { 728 | name: 'entity.other.inherited-class.prelude.$1.haskell' 729 | match: "{lb}(#{prelude.classes.join('|')}){rb}" 730 | } 731 | { 732 | name: 'support.class.prelude.$1.haskell' 733 | match: "{lb}(#{prelude.types.join('|')}){rb}" 734 | } 735 | ] 736 | type_ctor: 737 | name: 'entity.name.tag.haskell' 738 | match: /{lb}{className}{rb}/ 739 | captures: 0: patterns: [ 740 | { include: '#module_name_prefix' } 741 | { 742 | name: 'support.tag.prelude.$1.haskell' 743 | match: "{lb}(#{prelude.constr.join('|')}){rb}" 744 | } 745 | ] 746 | where: 747 | match: '{lb}where{rb}' 748 | name: 'keyword.other.haskell' 749 | family_and_instance: 750 | match: '{lb}(family|instance){rb}' 751 | name: 'keyword.other.haskell' 752 | invalid: 753 | match: /\S+/ 754 | name: 'invalid.illegal.character-not-allowed-here.haskell' 755 | function_name: 756 | name: 'entity.name.function.haskell' 757 | match: /{lb}{functionName}{rb}/ 758 | captures: 0: patterns: [ 759 | { include: '#module_name_prefix' } 760 | ] 761 | assignment_op: 762 | match: /=/ 763 | captures: 764 | 0: name: 'keyword.operator.assignment.haskell' 765 | attribute_name: 766 | name: 'entity.other.attribute-name.haskell' 767 | match: /{lb}{functionName}{rb}/ 768 | liquidhaskell_annotation: 769 | name: 'block.liquidhaskell' 770 | contentName: 'block.liquidhaskell.annotation' 771 | begin: '\\{-@(?!#)' 772 | end: '@-\\}' 773 | patterns: [ 774 | { include: 'annotation.liquidhaskell.haskell' } 775 | ] 776 | type_application: 777 | name: 'other.type-application.haskell' 778 | match: "( 2 | if typeof rx is 'object' 3 | rx.source 4 | else 5 | rx 6 | 7 | list = (s, sep) -> 8 | # "(?<#{item}>(?:#{rxToStr s})(?:\\s*(?:#{rxToStr sep})\\s*\\g<#{item}>)?)" 9 | "((?:#{rxToStr s})(?:(?:#{rxToStr sep})(?:#{rxToStr s}))*)" 10 | 11 | listMaybe = (s, sep) -> 12 | # "(?<#{item}>(?:#{rxToStr s})(?:\\s*(?:#{rxToStr sep})\\s*\\g<#{item}>)?)?" 13 | "#{list(s, sep)}?" 14 | 15 | concat = (list...) -> 16 | r = ''.concat (list.map (i) -> "(?:#{rxToStr i})")... 17 | "(?:#{r})" 18 | 19 | balanced = (name, left, right) -> 20 | "(?<#{name}>(?:(?!#{left}|#{right}).|#{left}\\g<#{name}>#{right})*)" 21 | 22 | floatPattern = (digit, exp) -> 23 | exponent = "#{exp}[+-]?[0-9_]+" 24 | "#{digit}*(?:\\.#{digit}+(?:#{exponent})?|#{exponent})" 25 | 26 | guarded = (pattern) -> 27 | "(?:(? 9 | lines = 10 | input.map (line) -> 11 | line.split(/ :: /)[0] or line 12 | return lines 13 | 14 | run = (filter) -> 15 | if typeof filter isnt "function" 16 | rx = filter 17 | filter = (line) -> line.match(rx) 18 | p = arr(genp.filter filter) 19 | return p 20 | 21 | exp = {} 22 | 23 | exp.classes = run /class/ 24 | exp.funct = run /^[a-z]/ 25 | exp.constr = run /^[A-Z].*from:/ 26 | exp.types = run /data|type/ 27 | exp.operators = run(/^\(/).map((x) -> x.slice(1, -1)) 28 | 29 | exp.types.push (run (line) -> 30 | line.match(/^[A-Z]/) and not line.match(/::/) and not line.match(/True|False/))... 31 | exp.constr.push 'True', 'False' 32 | 33 | output = [ '# coffeelint: disable' ] 34 | for k, v of exp 35 | output.push "#{k} = #{JSON.stringify(v)}" 36 | output.push "module.exports = { #{Object.keys(exp)} }" 37 | 38 | fs.writeFileSync 'src/include/prelude.coffee', output.join('\n')+'\n', 'utf8' 39 | -------------------------------------------------------------------------------- /src/syntax-tools.coffee: -------------------------------------------------------------------------------- 1 | """ 2 | $ curl https://raw.githubusercontent.com/twilson63/cakefile-template/master/Cakefile > ../Cakefile 3 | 4 | $ cd .. && coffee -c -o lib src/main.coffee 5 | $ cd .. && npm version minor 6 | $ cd .. && git comm 7 | $ cd .. && cake build 8 | """ 9 | 10 | # Transforms an easy grammar specification object into a tmLanguage grammar 11 | # specification object. 12 | class GrammarCreator 13 | constructor: (@grammar, @print = false) -> 14 | 15 | process: -> 16 | grammar = @grammar 17 | print = @print 18 | G = {} 19 | 20 | for n in [ "comment", "fileTypes", "firstLineMatch", "keyEquivalent", "name", "scopeName", "injectionSelector" ] 21 | G[n] = grammar[n] if grammar[n]? 22 | 23 | {@autoAppendScopeName, @macros} = grammar 24 | 25 | @autoAppendScopeName = true if typeof @autoAppendScopeName is "undefined" 26 | @macros = {} if typeof @macros is "undefined" 27 | @grammarScopeName = G.scopeName.replace /.*\./, '' 28 | 29 | @hasGrammarScopeName = new RegExp "\\.#{@grammarScopeName}$" 30 | 31 | macros = @macros 32 | 33 | # make regexes to strings 34 | for k,v of macros 35 | if v instanceof RegExp 36 | macros[k] = v.source 37 | 38 | # resolve macros 39 | for k,v of macros 40 | macros[k] = @resolveMacros(v) 41 | 42 | loop 43 | all_done = true 44 | for k,v of macros 45 | macros[k] = @resolveMacros(v) 46 | 47 | if /\{[a-zA-Z_]\w*\}/.test(macros[k]) 48 | all_done = false 49 | if v == macros[k] 50 | all_done = true 51 | # throw "unresolved macro in #{v}" 52 | 53 | if all_done 54 | break 55 | 56 | name = grammar['name'] 57 | for k,v of @makePattern(grammar) 58 | G[k] = v 59 | 60 | G['name'] = name 61 | 62 | if grammar.repository? 63 | G.repository = {} 64 | for k,v of grammar.repository 65 | pats = @makePattern(v, macros) 66 | if pats.begin? or pats.match? 67 | pats = { "patterns": [ pats ] } 68 | else if pats instanceof Array 69 | pats = { "patterns": pats } 70 | 71 | G.repository[k] = pats 72 | 73 | if print 74 | if print.match /\.cson$/ 75 | CSON = require "season" 76 | fs = require "fs" 77 | 78 | fs.writeFileSync print, CSON.stringify(G) 79 | 80 | else if print.match /\.json$/ 81 | fs.writeFileSync print, JSON.stringify(G, null, " ") 82 | 83 | else if print == "CSON" 84 | CSON = require "season" 85 | process.stdout.write CSON.stringify(G) 86 | 87 | else 88 | process.stdout.write JSON.stringify(G, null, " ") 89 | 90 | G 91 | 92 | resolveMacros: (regex) -> 93 | if regex instanceof RegExp 94 | regex = regex.source 95 | 96 | macros = @macros 97 | 98 | regex.replace /// \{\w+\} ///g, (mob) -> 99 | s = mob[1...-1] 100 | 101 | if typeof macros[s] isnt "undefined" 102 | macros[s] 103 | else 104 | mob 105 | 106 | makeScopeName: (name) -> 107 | name = @resolveMacros(name) 108 | if @autoAppendScopeName 109 | unless @hasGrammarScopeName.test(name) 110 | return "#{name}.#{@grammarScopeName}" 111 | 112 | name 113 | 114 | # Transforms an easy grammar specification object into a tmLanguage grammar 115 | # specification object. 116 | # 117 | # n -> name 118 | # N -> contentName 119 | # p -> patterns 120 | # i -> include 121 | # m -> match 122 | # b -> begin 123 | # e -> end 124 | # c -> captures/beginCaptures 125 | # C -> endCaptures 126 | # L -> applyEndPatternLast 127 | # 128 | makePattern: (pattern) -> 129 | pat = pattern 130 | P = {} 131 | 132 | if typeof pattern == "string" 133 | P.include = pattern 134 | return P 135 | 136 | if pattern instanceof Array 137 | return (@makePattern(p) for p in pattern) 138 | 139 | for k,v of pat 140 | switch k 141 | when "N", "contentName" 142 | P.contentName = @makeScopeName(v) 143 | when "i", "include" 144 | P.include = v 145 | when "n", "name" 146 | P.name = @makeScopeName(v) 147 | when "m", "match" 148 | P.match = @resolveMacros(v) 149 | when "b", "begin" 150 | P.begin = @resolveMacros(v) 151 | when "e", "end" 152 | P.end = @resolveMacros(v) 153 | 154 | when "c", "captures", "beginCaptures" 155 | if P.begin? 156 | P.beginCaptures = c = {} 157 | else 158 | P.captures = c = {} 159 | 160 | if typeof v == "string" 161 | c[0] = { name: @makeScopeName(v) } 162 | else 163 | for ck,cv of v 164 | if typeof cv isnt "string" 165 | c[ck] = @makePattern(cv) 166 | else 167 | c[ck] = { name: @makeScopeName(cv) } 168 | 169 | when "C", "endCaptures" 170 | P.endCaptures = c = {} 171 | if typeof v == "string" 172 | c[0] = { name: @makeScopeName(v) } 173 | else 174 | for ck,cv of v 175 | if typeof cv isnt "string" 176 | c[ck] = @makePattern(cv) 177 | else 178 | c[ck] = { name: @makeScopeName(cv) } 179 | 180 | when "p", "patterns" 181 | unless v instanceof Array 182 | v = [ v ] 183 | P.patterns = (@makePattern(p) for p in v) 184 | 185 | when "L", "applyEndPatternLast" 186 | P.applyEndPatternLast = v 187 | 188 | else 189 | P[k] = v 190 | 191 | P 192 | 193 | makeGrammar = (print, grammar) -> 194 | grammar_ = (require 'clone')(grammar) 195 | (new GrammarCreator grammar_, print).process() 196 | 197 | include = (what) -> require "./include/#{what}" 198 | 199 | 200 | module.exports = { makeGrammar, include } 201 | -------------------------------------------------------------------------------- /styles/overrides.less: -------------------------------------------------------------------------------- 1 | @import "syntax-variables"; 2 | .syntax--keyword.syntax--type.syntax--haskell { 3 | color: @syntax-color-keyword; 4 | } 5 | --------------------------------------------------------------------------------