├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .release-please-manifest.json ├── README.md ├── editor-extensions └── vscode │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── .vscode │ ├── extensions.json │ ├── launch.json │ ├── settings.json │ └── tasks.json │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── grain_shorthand_white.png │ ├── icons │ └── grain_shorthand_color.svg │ ├── language-configuration.json │ ├── package-lock.json │ ├── package.json │ ├── scripts │ └── e2e.sh │ ├── src │ ├── GrainDocCompletionProvider.ts │ ├── GrainErrorHandler.ts │ └── extension.ts │ ├── syntaxes │ ├── generate-grain-type.js │ └── grain.json │ ├── test │ ├── fixture │ │ ├── completion.txt │ │ └── diagnostics.txt │ ├── helper.ts │ ├── index.ts │ └── runTest.ts │ └── tsconfig.json ├── grain_shorthand_white.png └── release-please-config.json /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Language Server CI Workflow 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | name: Check format 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Setup Node.js 11 | uses: actions/setup-node@v2.1.2 12 | with: 13 | node-version: '14' 14 | check-latest: true 15 | 16 | - name: Checkout project 17 | uses: actions/checkout@v2 18 | 19 | - name: Run npm install 20 | working-directory: editor-extensions/vscode 21 | run: npm ci 22 | 23 | - name: Check formatting 24 | run: npm run check-format 25 | working-directory: editor-extensions/vscode 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release & publish to marketplaces 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | release-please: 9 | name: Create Release 10 | runs-on: ubuntu-latest 11 | outputs: 12 | # Remap this name because it is quite confusing 13 | release_created: ${{ steps.release.outputs.releases_created }} 14 | steps: 15 | - uses: GoogleCloudPlatform/release-please-action@v2 16 | id: release 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | command: manifest 20 | 21 | publish: 22 | needs: release-please 23 | if: ${{ needs.release-please.outputs.release_created }} 24 | name: Publish to Microsoft and OVSX marketplaces 25 | runs-on: ${{ matrix.os }} 26 | 27 | strategy: 28 | matrix: 29 | os: [ubuntu-latest] 30 | 31 | defaults: 32 | run: 33 | working-directory: editor-extensions/vscode/ 34 | 35 | steps: 36 | - name: Setup Node.js 37 | uses: actions/setup-node@v2.1.2 38 | with: 39 | node-version: "14" 40 | check-latest: true 41 | 42 | - name: Checkout project 43 | uses: actions/checkout@v2 44 | 45 | - name: Install dependencies 46 | run: npm install 47 | 48 | - name: Publish it 49 | run: npm run deploy 50 | env: 51 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 52 | OVSX_PAT: ${{ secrets.OVSX_PAT }} 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | *.gr 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | {"editor-extensions/vscode":"0.20.0"} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Editor extension support for Grain 2 | 3 | Grain has a standard LSP implementation built into the compiler. 4 | 5 | This repository is a collection of editor extensions for adding language support for [Grain](grain-lang.org). 6 | 7 | Our first extension is for Visual Studio Code. 8 | 9 | ## Contributing 10 | 11 | Contributions are welcome and greatly appreciated. If you find issues while using this extension, please report them by opening an issue in our [issue tracker](https://github.com/grain-lang/grain-language-servers/issues). 12 | 13 | For more in-depth information on how to contribute to Grain's projects, check out the official [contributing guide](https://github.com/grain-lang/grain/blob/main/CONTRIBUTING.md). 14 | -------------------------------------------------------------------------------- /editor-extensions/vscode/.gitignore: -------------------------------------------------------------------------------- 1 | *.vsix 2 | out 3 | syntaxes/grain-type.json 4 | -------------------------------------------------------------------------------- /editor-extensions/vscode/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=true 2 | -------------------------------------------------------------------------------- /editor-extensions/vscode/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | out/ 3 | CHANGELOG.md 4 | -------------------------------------------------------------------------------- /editor-extensions/vscode/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": ["dbaeumer.vscode-eslint"] 7 | } 8 | -------------------------------------------------------------------------------- /editor-extensions/vscode/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "name": "Launch Extension", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"], 11 | "outFiles": ["${workspaceRoot}/out/**/*.js"], 12 | "preLaunchTask": "${defaultBuildTask}" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /editor-extensions/vscode/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.insertSpaces": false, 3 | "typescript.tsc.autoDetect": "off", 4 | "typescript.preferences.quoteStyle": "single", 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll.eslint": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /editor-extensions/vscode/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "esbuild-watch", 7 | "group": { 8 | "kind": "build", 9 | "isDefault": true 10 | }, 11 | // https://github.com/connor4312/esbuild-problem-matchers/blob/0cd222036a0f0dcccda4df35d094d2882b93ddc1/package.json#L28-L67 12 | "problemMatcher": [ 13 | { 14 | "severity": "error", 15 | "applyTo": "closedDocuments", 16 | "source": "esbuild", 17 | "owner": "typescript", 18 | "fileLocation": "relative", 19 | "pattern": [ 20 | { 21 | "regexp": "> (.*?):([0-9]+):([0-9]+): (warning|error): (.+)$", 22 | "file": 1, 23 | "line": 2, 24 | "column": 3, 25 | "severity": 4, 26 | "message": 5 27 | } 28 | ], 29 | "background": { 30 | "activeOnStart": true, 31 | "beginsPattern": { 32 | "regexp": "\\[watch\\] build started" 33 | }, 34 | "endsPattern": { 35 | "regexp": "\\[watch\\] build finished" 36 | } 37 | } 38 | } 39 | ], 40 | "label": "npm: esbuild-watch", 41 | "detail": "npm run esbuild-base -- --sourcemap --watch", 42 | "isBackground": true 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /editor-extensions/vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | node_modules/ 3 | out/*.js.map 4 | scripts/ 5 | src/ 6 | test/ 7 | .gitignore 8 | .npmrc 9 | .prettierignore 10 | CHANGELOG.md 11 | package-lock.json 12 | tsconfig.json 13 | *.vsix 14 | -------------------------------------------------------------------------------- /editor-extensions/vscode/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to the `vscode-grain` extension will be documented in this file. 3 | 4 | ## [0.20.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.19.0...vscode-grain-v0.20.0) (2024-03-27) 5 | 6 | 7 | ### Features 8 | 9 | * Support [@throws](https://www.github.com/throws) graindoc ([#175](https://www.github.com/grain-lang/grain-language-server/issues/175)) ([33c3aa0](https://www.github.com/grain-lang/grain-language-server/commit/33c3aa06237c05788998d5b33715a792b6eed6bc)) 10 | 11 | ## [0.19.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.18.2...vscode-grain-v0.19.0) (2024-03-04) 12 | 13 | 14 | ### ⚠ BREAKING CHANGES 15 | 16 | * Replace grainfind with cliFlags setting (#113) 17 | * Upgrade dependencies & require much newer VSCode 18 | * Remove unused plugin options 19 | * Rename settings namespace to just "grain" 20 | * Implement plugin as only an LSP client (#100) 21 | * Add formatter support (#85) 22 | 23 | ### Features 24 | 25 | * Add better autocompletion on block comments ([#168](https://www.github.com/grain-lang/grain-language-server/issues/168)) ([c6e141a](https://www.github.com/grain-lang/grain-language-server/commit/c6e141a9a7861028c2f9f524f560eabadd4c31dc)) 26 | * Add bigint syntax highlighting ([#123](https://www.github.com/grain-lang/grain-language-server/issues/123)) ([dab8981](https://www.github.com/grain-lang/grain-language-server/commit/dab89815ba7a1d30300b01dd70136071cf74decb)) 27 | * Add formatter support ([#85](https://www.github.com/grain-lang/grain-language-server/issues/85)) ([c1a85e4](https://www.github.com/grain-lang/grain-language-server/commit/c1a85e457e3ec5dee3c1ed6d113bbd2c0ade17e4)) 28 | * Add LSP restart command ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 29 | * Add support for new float syntax ([#163](https://www.github.com/grain-lang/grain-language-server/issues/163)) ([44851bc](https://www.github.com/grain-lang/grain-language-server/commit/44851bcc64e26b30ec954c738467f9fec7ce71d9)) 30 | * Add syntax highlighting for pattern constraints ([#61](https://www.github.com/grain-lang/grain-language-server/issues/61)) ([ecbb40d](https://www.github.com/grain-lang/grain-language-server/commit/ecbb40d4aa7835d418eea173393cea0e615341a1)) 31 | * Basic syntax highlighting for type aliases ([#97](https://www.github.com/grain-lang/grain-language-server/issues/97)) ([8af29fd](https://www.github.com/grain-lang/grain-language-server/commit/8af29fd2dde6a06c26da41b9065a953e87d99dac)) 32 | * Build extension into a single JS file ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 33 | * Check for the existence of a build script and use that if it exists ([#91](https://www.github.com/grain-lang/grain-language-server/issues/91)) ([c603a60](https://www.github.com/grain-lang/grain-language-server/commit/c603a6038ae11653d3076cf8accb0f6e42d8473c)) 34 | * Contribute file icons to grain files ([#109](https://www.github.com/grain-lang/grain-language-server/issues/109)) ([77d6691](https://www.github.com/grain-lang/grain-language-server/commit/77d66911ee81e2617da0caa7b64cd1fc2e78d190)) 35 | * **docblock:** Continue to add * inside doc comments after return ([#83](https://www.github.com/grain-lang/grain-language-server/issues/83)) ([3f96f20](https://www.github.com/grain-lang/grain-language-server/commit/3f96f20cbe258e4a8b02cede9d421e1fffc8ee13)) 36 | * Exception highlighting ([#171](https://www.github.com/grain-lang/grain-language-server/issues/171)) ([59ccb76](https://www.github.com/grain-lang/grain-language-server/commit/59ccb763ca40d2d7c6c6de1e5a204b5a778ed789)) 37 | * Graindoc highlighting ([#132](https://www.github.com/grain-lang/grain-language-server/issues/132)) ([11a61af](https://www.github.com/grain-lang/grain-language-server/commit/11a61affa9b51c159b287813fa0beb0cca5b36ee)) 38 | * Implement plugin as only an LSP client ([#100](https://www.github.com/grain-lang/grain-language-server/issues/100)) ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 39 | * parse warnings from code returned from LSP ([#70](https://www.github.com/grain-lang/grain-language-server/issues/70)) ([78a69cb](https://www.github.com/grain-lang/grain-language-server/commit/78a69cb3625b910cd403ddc436ffcb4ff71913f5)) 40 | * Remove the block on CRLF format ([#72](https://www.github.com/grain-lang/grain-language-server/issues/72)) ([905e18e](https://www.github.com/grain-lang/grain-language-server/commit/905e18ea171ec553c45a43d043881bca2ae41423)) 41 | * Replace grainfind with cliFlags setting ([#113](https://www.github.com/grain-lang/grain-language-server/issues/113)) ([68c1816](https://www.github.com/grain-lang/grain-language-server/commit/68c1816f9169f7fc250c97e5da2bffe1d3e3f49d)) 42 | * Search for a variety of grain CLIs in PATH ([#111](https://www.github.com/grain-lang/grain-language-server/issues/111)) ([28e1e9c](https://www.github.com/grain-lang/grain-language-server/commit/28e1e9c5ec62f662feedadc94d8d72e3f55709c8)) 43 | * Support `Infinity` and `NaN` keywords ([#142](https://www.github.com/grain-lang/grain-language-server/issues/142)) ([d940a07](https://www.github.com/grain-lang/grain-language-server/commit/d940a07a34b2302efd1d6c59354e7b81126403b0)) 44 | * Support and keyword for data types ([#172](https://www.github.com/grain-lang/grain-language-server/issues/172)) ([a8b556e](https://www.github.com/grain-lang/grain-language-server/commit/a8b556ee879e7e2dc077fb0becf495bb876b54fe)) 45 | * Support custom operators ([#149](https://www.github.com/grain-lang/grain-language-server/issues/149)) ([4b4c75e](https://www.github.com/grain-lang/grain-language-server/commit/4b4c75e6b517f27509e323cffb3485ed60a58013)) 46 | * Support multiple workspaces & individual files ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 47 | * Support new module system ([#151](https://www.github.com/grain-lang/grain-language-server/issues/151)) ([5240131](https://www.github.com/grain-lang/grain-language-server/commit/524013101c8dc843ca8693221c2d606cef23f533)) 48 | * Support operator identifiers ([#174](https://www.github.com/grain-lang/grain-language-server/issues/174)) ([5fb6a32](https://www.github.com/grain-lang/grain-language-server/commit/5fb6a32be9a15663eeca431f07e368a7abd47cab)) 49 | * Support or-pattern and alias syntax ([#116](https://www.github.com/grain-lang/grain-language-server/issues/116)) ([38e8ed8](https://www.github.com/grain-lang/grain-language-server/commit/38e8ed8698d943e5eb9618efc5a99628e53f4d44)) 50 | * Support short and unsigned ints ([#150](https://www.github.com/grain-lang/grain-language-server/issues/150)) ([9998689](https://www.github.com/grain-lang/grain-language-server/commit/999868996f8328210fb6c35f649aa169c73ef4f9)) 51 | * Support the `return` keyword ([#146](https://www.github.com/grain-lang/grain-language-server/issues/146)) ([c540e52](https://www.github.com/grain-lang/grain-language-server/commit/c540e52ba401f90b1866b3489e2d116795293ca0)) 52 | * Switch to vscode-url library to better support Windows paths ([#62](https://www.github.com/grain-lang/grain-language-server/issues/62)) ([66cfa08](https://www.github.com/grain-lang/grain-language-server/commit/66cfa08c7cd5da538d19e3dd63cb490490fa3d08)) 53 | * Update module include syntax ([#170](https://www.github.com/grain-lang/grain-language-server/issues/170)) ([13f5269](https://www.github.com/grain-lang/grain-language-server/commit/13f52692f8e414fc2feb1fb856ab5d4ec53a2060)) 54 | * Update scoping operator syntax ([#169](https://www.github.com/grain-lang/grain-language-server/issues/169)) ([e738bb0](https://www.github.com/grain-lang/grain-language-server/commit/e738bb0728ebe0a8ef8abd0231e597871b69ed4c)) 55 | * **vscode:** Add completions for Grain Doc comments ([#80](https://www.github.com/grain-lang/grain-language-server/issues/80)) ([d34b4ec](https://www.github.com/grain-lang/grain-language-server/commit/d34b4ec89a4dd20a393c09b4d783c8704345c04f)) 56 | 57 | 58 | ### Bug Fixes 59 | 60 | * Add naive mutex around config change events ([#125](https://www.github.com/grain-lang/grain-language-server/issues/125)) ([2dd2068](https://www.github.com/grain-lang/grain-language-server/commit/2dd2068d19a857f33f8e8fa3bed12d0bc3ab4eff)) 61 | * Adjust icon size ([#119](https://www.github.com/grain-lang/grain-language-server/issues/119)) ([5f7ef16](https://www.github.com/grain-lang/grain-language-server/commit/5f7ef16f32e1b2dc3cd51efa247d5257a2cd47db)) 62 | * Avoid handling output scheme messages ([#114](https://www.github.com/grain-lang/grain-language-server/issues/114)) ([b71c7ad](https://www.github.com/grain-lang/grain-language-server/commit/b71c7ada6246d639885755f6413df67fef768441)) 63 | * Correct the executable path ([#87](https://www.github.com/grain-lang/grain-language-server/issues/87)) ([cc07e9d](https://www.github.com/grain-lang/grain-language-server/commit/cc07e9d69b4b0448cbd61f33ecc59ae2163a1570)) 64 | * Correctly highlight block comments ([#135](https://www.github.com/grain-lang/grain-language-server/issues/135)) ([3d31997](https://www.github.com/grain-lang/grain-language-server/commit/3d319971cacb12224996e5aa617656287c88679a)) 65 | * Correctly highlight function type annotations in type vectors ([#131](https://www.github.com/grain-lang/grain-language-server/issues/131)) ([5b025e9](https://www.github.com/grain-lang/grain-language-server/commit/5b025e95351df8f824142abfa256f3c8325caed6)) 66 | * Correctly highlight type hovers ([#128](https://www.github.com/grain-lang/grain-language-server/issues/128)) ([51d3e44](https://www.github.com/grain-lang/grain-language-server/commit/51d3e44c9a835983e646f1bcd4e97a35c7180c55)) 67 | * enable neovim ([#76](https://www.github.com/grain-lang/grain-language-server/issues/76)) ([7257f21](https://www.github.com/grain-lang/grain-language-server/commit/7257f21526bbfbbedc1bf8aeadb07c0025fbe393)) 68 | * Fix the build issue ([#95](https://www.github.com/grain-lang/grain-language-server/issues/95)) ([4a30b83](https://www.github.com/grain-lang/grain-language-server/commit/4a30b831ec5e7e678886154f36c42169abe1e8d3)) 69 | * Highlight match keyword correctly ([#121](https://www.github.com/grain-lang/grain-language-server/issues/121)) ([3b40fc4](https://www.github.com/grain-lang/grain-language-server/commit/3b40fc417d6a28230eeeaf32757fb6c6c7740466)) 70 | * Highlight module scoped types properly ([#122](https://www.github.com/grain-lang/grain-language-server/issues/122)) ([e046b6b](https://www.github.com/grain-lang/grain-language-server/commit/e046b6bd999274d85258c020eca7d7614939be90)) 71 | * No longer recommend \n for grain files ([#74](https://www.github.com/grain-lang/grain-language-server/issues/74)) ([e5ae4a5](https://www.github.com/grain-lang/grain-language-server/commit/e5ae4a50d15a5afe5d3a2d40bc6570b55994aecb)) 72 | * Only highlight valid string escapes ([#118](https://www.github.com/grain-lang/grain-language-server/issues/118)) ([d82dea3](https://www.github.com/grain-lang/grain-language-server/commit/d82dea382865f939711567805333c4721ac728fc)) 73 | * proper token types for storage keywords ([#82](https://www.github.com/grain-lang/grain-language-server/issues/82)) ([bfde3af](https://www.github.com/grain-lang/grain-language-server/commit/bfde3af7d62652ee82bb0b0cc39b26ddb2ebf118)) 74 | * Properly highlight `isnt` ([#137](https://www.github.com/grain-lang/grain-language-server/issues/137)) ([8c4bc7b](https://www.github.com/grain-lang/grain-language-server/commit/8c4bc7b1f1d89f549283b018ef88ab30954c4b79)) 75 | * Properly highlight true/false in pattern matching ([#117](https://www.github.com/grain-lang/grain-language-server/issues/117)) ([43d2cba](https://www.github.com/grain-lang/grain-language-server/commit/43d2cba033c3396fb86cb47fa5e55b8aa5fae268)) 76 | * Use consistent scope for type variables ([#173](https://www.github.com/grain-lang/grain-language-server/issues/173)) ([ed4b370](https://www.github.com/grain-lang/grain-language-server/commit/ed4b3704de668d201d591c5d54415aefed1a3d2d)) 77 | 78 | 79 | ### Miscellaneous Chores 80 | 81 | * Remove unused plugin options ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 82 | * Rename settings namespace to just "grain" ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 83 | * Upgrade dependencies & require much newer VSCode ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 84 | 85 | ### [0.18.2](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.18.1...vscode-grain-v0.18.2) (2022-08-01) 86 | 87 | 88 | ### Bug Fixes 89 | 90 | * Properly highlight `isnt` ([#137](https://www.github.com/grain-lang/grain-language-server/issues/137)) ([8c4bc7b](https://www.github.com/grain-lang/grain-language-server/commit/8c4bc7b1f1d89f549283b018ef88ab30954c4b79)) 91 | 92 | ### [0.18.1](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.18.0...vscode-grain-v0.18.1) (2022-07-26) 93 | 94 | 95 | ### Bug Fixes 96 | 97 | * Correctly highlight block comments ([#135](https://www.github.com/grain-lang/grain-language-server/issues/135)) ([3d31997](https://www.github.com/grain-lang/grain-language-server/commit/3d319971cacb12224996e5aa617656287c88679a)) 98 | 99 | ## [0.18.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.17.2...vscode-grain-v0.18.0) (2022-07-26) 100 | 101 | 102 | ### Features 103 | 104 | * Graindoc highlighting ([#132](https://www.github.com/grain-lang/grain-language-server/issues/132)) ([11a61af](https://www.github.com/grain-lang/grain-language-server/commit/11a61affa9b51c159b287813fa0beb0cca5b36ee)) 105 | 106 | 107 | ### Bug Fixes 108 | 109 | * Correctly highlight function type annotations in type vectors ([#131](https://www.github.com/grain-lang/grain-language-server/issues/131)) ([5b025e9](https://www.github.com/grain-lang/grain-language-server/commit/5b025e95351df8f824142abfa256f3c8325caed6)) 110 | 111 | ### [0.17.2](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.17.1...vscode-grain-v0.17.2) (2022-07-15) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * Correctly highlight type hovers ([#128](https://www.github.com/grain-lang/grain-language-server/issues/128)) ([51d3e44](https://www.github.com/grain-lang/grain-language-server/commit/51d3e44c9a835983e646f1bcd4e97a35c7180c55)) 117 | 118 | ### [0.17.1](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.17.0...vscode-grain-v0.17.1) (2022-07-01) 119 | 120 | 121 | ### Bug Fixes 122 | 123 | * Add naive mutex around config change events ([#125](https://www.github.com/grain-lang/grain-language-server/issues/125)) ([2dd2068](https://www.github.com/grain-lang/grain-language-server/commit/2dd2068d19a857f33f8e8fa3bed12d0bc3ab4eff)) 124 | 125 | ## [0.17.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.16.1...vscode-grain-v0.17.0) (2022-06-28) 126 | 127 | 128 | ### Features 129 | 130 | * Add bigint syntax highlighting ([#123](https://www.github.com/grain-lang/grain-language-server/issues/123)) ([dab8981](https://www.github.com/grain-lang/grain-language-server/commit/dab89815ba7a1d30300b01dd70136071cf74decb)) 131 | 132 | ### [0.16.1](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.16.0...vscode-grain-v0.16.1) (2022-06-13) 133 | 134 | 135 | ### Bug Fixes 136 | 137 | * Adjust icon size ([#119](https://www.github.com/grain-lang/grain-language-server/issues/119)) ([5f7ef16](https://www.github.com/grain-lang/grain-language-server/commit/5f7ef16f32e1b2dc3cd51efa247d5257a2cd47db)) 138 | * Highlight match keyword correctly ([#121](https://www.github.com/grain-lang/grain-language-server/issues/121)) ([3b40fc4](https://www.github.com/grain-lang/grain-language-server/commit/3b40fc417d6a28230eeeaf32757fb6c6c7740466)) 139 | * Highlight module scoped types properly ([#122](https://www.github.com/grain-lang/grain-language-server/issues/122)) ([e046b6b](https://www.github.com/grain-lang/grain-language-server/commit/e046b6bd999274d85258c020eca7d7614939be90)) 140 | 141 | ## [0.16.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.15.0...vscode-grain-v0.16.0) (2022-06-06) 142 | 143 | 144 | ### ⚠ BREAKING CHANGES 145 | 146 | * Replace grainfind with cliFlags setting (#113) 147 | * Upgrade dependencies & require much newer VSCode 148 | * Remove unused plugin options 149 | * Rename settings namespace to just "grain" 150 | * Implement plugin as only an LSP client (#100) 151 | 152 | ### Features 153 | 154 | * Add LSP restart command ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 155 | * Build extension into a single JS file ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 156 | * Contribute file icons to grain files ([#109](https://www.github.com/grain-lang/grain-language-server/issues/109)) ([77d6691](https://www.github.com/grain-lang/grain-language-server/commit/77d66911ee81e2617da0caa7b64cd1fc2e78d190)) 157 | * Implement plugin as only an LSP client ([#100](https://www.github.com/grain-lang/grain-language-server/issues/100)) ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 158 | * Replace grainfind with cliFlags setting ([#113](https://www.github.com/grain-lang/grain-language-server/issues/113)) ([68c1816](https://www.github.com/grain-lang/grain-language-server/commit/68c1816f9169f7fc250c97e5da2bffe1d3e3f49d)) 159 | * Search for a variety of grain CLIs in PATH ([#111](https://www.github.com/grain-lang/grain-language-server/issues/111)) ([28e1e9c](https://www.github.com/grain-lang/grain-language-server/commit/28e1e9c5ec62f662feedadc94d8d72e3f55709c8)) 160 | * Support multiple workspaces & individual files ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 161 | * Support or-pattern and alias syntax ([#116](https://www.github.com/grain-lang/grain-language-server/issues/116)) ([38e8ed8](https://www.github.com/grain-lang/grain-language-server/commit/38e8ed8698d943e5eb9618efc5a99628e53f4d44)) 162 | 163 | 164 | ### Bug Fixes 165 | 166 | * Avoid handling output scheme messages ([#114](https://www.github.com/grain-lang/grain-language-server/issues/114)) ([b71c7ad](https://www.github.com/grain-lang/grain-language-server/commit/b71c7ada6246d639885755f6413df67fef768441)) 167 | * Only highlight valid string escapes ([#118](https://www.github.com/grain-lang/grain-language-server/issues/118)) ([d82dea3](https://www.github.com/grain-lang/grain-language-server/commit/d82dea382865f939711567805333c4721ac728fc)) 168 | * Properly highlight true/false in pattern matching ([#117](https://www.github.com/grain-lang/grain-language-server/issues/117)) ([43d2cba](https://www.github.com/grain-lang/grain-language-server/commit/43d2cba033c3396fb86cb47fa5e55b8aa5fae268)) 169 | 170 | 171 | ### Miscellaneous Chores 172 | 173 | * Remove unused plugin options ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 174 | * Rename settings namespace to just "grain" ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 175 | * Upgrade dependencies & require much newer VSCode ([8c12b49](https://www.github.com/grain-lang/grain-language-server/commit/8c12b494641fddad4b2488b53febbc312802390c)) 176 | 177 | ## [0.15.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.14.1...vscode-grain-v0.15.0) (2021-10-26) 178 | 179 | 180 | ### Features 181 | 182 | * Basic syntax highlighting for type aliases ([#97](https://www.github.com/grain-lang/grain-language-server/issues/97)) ([8af29fd](https://www.github.com/grain-lang/grain-language-server/commit/8af29fd2dde6a06c26da41b9065a953e87d99dac)) 183 | 184 | ### [0.14.1](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.14.0...vscode-grain-v0.14.1) (2021-10-12) 185 | 186 | 187 | ### Bug Fixes 188 | 189 | * Fix the build issue ([#95](https://www.github.com/grain-lang/grain-language-server/issues/95)) ([4a30b83](https://www.github.com/grain-lang/grain-language-server/commit/4a30b831ec5e7e678886154f36c42169abe1e8d3)) 190 | 191 | ## [0.14.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.13.1...vscode-grain-v0.14.0) (2021-10-12) 192 | 193 | 194 | ### Features 195 | 196 | * Check for the existence of a build script and use that if it exists ([#91](https://www.github.com/grain-lang/grain-language-server/issues/91)) ([c603a60](https://www.github.com/grain-lang/grain-language-server/commit/c603a6038ae11653d3076cf8accb0f6e42d8473c)) 197 | 198 | ### [0.13.1](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.13.0...vscode-grain-v0.13.1) (2021-09-07) 199 | 200 | 201 | ### Bug Fixes 202 | 203 | * Correct the executable path ([#87](https://www.github.com/grain-lang/grain-language-server/issues/87)) ([cc07e9d](https://www.github.com/grain-lang/grain-language-server/commit/cc07e9d69b4b0448cbd61f33ecc59ae2163a1570)) 204 | 205 | ## [0.13.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.12.0...vscode-grain-v0.13.0) (2021-09-04) 206 | 207 | 208 | ### ⚠ BREAKING CHANGES 209 | 210 | * Add formatter support (#85) 211 | 212 | ### Features 213 | 214 | * Add formatter support ([#85](https://www.github.com/grain-lang/grain-language-server/issues/85)) ([c1a85e4](https://www.github.com/grain-lang/grain-language-server/commit/c1a85e457e3ec5dee3c1ed6d113bbd2c0ade17e4)) 215 | 216 | ## [0.12.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.11.1...vscode-grain-v0.12.0) (2021-06-24) 217 | 218 | 219 | ### Features 220 | 221 | * **docblock:** Continue to add * inside doc comments after return ([#83](https://www.github.com/grain-lang/grain-language-server/issues/83)) ([3f96f20](https://www.github.com/grain-lang/grain-language-server/commit/3f96f20cbe258e4a8b02cede9d421e1fffc8ee13)) 222 | * **vscode:** Add completions for Grain Doc comments ([#80](https://www.github.com/grain-lang/grain-language-server/issues/80)) ([d34b4ec](https://www.github.com/grain-lang/grain-language-server/commit/d34b4ec89a4dd20a393c09b4d783c8704345c04f)) 223 | 224 | 225 | ### Bug Fixes 226 | 227 | * proper token types for storage keywords ([#82](https://www.github.com/grain-lang/grain-language-server/issues/82)) ([bfde3af](https://www.github.com/grain-lang/grain-language-server/commit/bfde3af7d62652ee82bb0b0cc39b26ddb2ebf118)) 228 | 229 | ### [0.11.1](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.11.0...vscode-grain-v0.11.1) (2021-06-10) 230 | 231 | 232 | ### Bug Fixes 233 | 234 | * enable neovim ([#76](https://www.github.com/grain-lang/grain-language-server/issues/76)) ([7257f21](https://www.github.com/grain-lang/grain-language-server/commit/7257f21526bbfbbedc1bf8aeadb07c0025fbe393)) 235 | * No longer recommend \n for grain files ([#74](https://www.github.com/grain-lang/grain-language-server/issues/74)) ([e5ae4a5](https://www.github.com/grain-lang/grain-language-server/commit/e5ae4a50d15a5afe5d3a2d40bc6570b55994aecb)) 236 | 237 | ## [0.11.0](https://www.github.com/grain-lang/grain-language-server/compare/vscode-grain-v0.10.3...vscode-grain-v0.11.0) (2021-05-30) 238 | 239 | 240 | ### Features 241 | 242 | * Add syntax highlighting for pattern constraints ([#61](https://www.github.com/grain-lang/grain-language-server/issues/61)) ([ecbb40d](https://www.github.com/grain-lang/grain-language-server/commit/ecbb40d4aa7835d418eea173393cea0e615341a1)) 243 | * parse warnings from code returned from LSP ([#70](https://www.github.com/grain-lang/grain-language-server/issues/70)) ([78a69cb](https://www.github.com/grain-lang/grain-language-server/commit/78a69cb3625b910cd403ddc436ffcb4ff71913f5)) 244 | * Remove the block on CRLF format ([#72](https://www.github.com/grain-lang/grain-language-server/issues/72)) ([905e18e](https://www.github.com/grain-lang/grain-language-server/commit/905e18ea171ec553c45a43d043881bca2ae41423)) 245 | * Switch to vscode-url library to better support Windows paths ([#62](https://www.github.com/grain-lang/grain-language-server/issues/62)) ([66cfa08](https://www.github.com/grain-lang/grain-language-server/commit/66cfa08c7cd5da538d19e3dd63cb490490fa3d08)) 246 | 247 | ## v0.10.3 248 | - Fixes syntax highlighting for rational literals. 249 | 250 | ## v0.10.2 251 | - Fixes syntax highlighting for floats in scientific notation. 252 | 253 | ## v0.10.1 254 | - Fixes an issue running the language server on Windows. 255 | 256 | ## v0.10.0 257 | - Adds syntax support for `for` loops. 258 | 259 | ## v0.9.0 260 | - Adds syntax support for more number literals. 261 | 262 | ## v0.8.1 263 | - Fixes an issue with using `and` as an identifier. 264 | 265 | ## v0.8.0 266 | - Adds syntax highlighting support for unicode string escapes, and fixes a number of other syntax highlighting issues. 267 | 268 | ## v0.7.0 269 | - Adds support for hover types. 270 | 271 | ## v0.6.3 272 | - Adds syntax highlighting support for bitwise operators and `and` bindings. 273 | 274 | ## v0.6.2 275 | - Minor fix where the lsp would not work properly on Windows. 276 | 277 | ## v0.6.1 278 | - Minor fix where code lenses were not picked up on Linux. 279 | 280 | ## v0.6.0 281 | - Code lenses! See the types of top-level statements in your editor. 282 | 283 | ## v0.5.1 284 | - Minor bugfix for launching LSP 285 | 286 | ## v0.5.0 287 | - Adds support for the LSP server which provides type checking 288 | 289 | ## v0.4.0 290 | - Adds support for the new comment syntax 291 | 292 | ## v0.2.0 293 | - Adds support for constant patterns when pattern matching 294 | 295 | ## v0.1.0 296 | - Initial release 297 | -------------------------------------------------------------------------------- /editor-extensions/vscode/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018-2020 Oscar Spencer & Contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /editor-extensions/vscode/README.md: -------------------------------------------------------------------------------- 1 | # VSCode extension for Grain 2 | 3 | The VSCode extension for the [Grain programming language](https://github.com/grain-lang/grain). 4 | 5 | **Requires:** vscode 1.67+ 6 | 7 | ## Functionality 8 | 9 | This LSP Protocol client provides the the following language features for grain (`.gr`) files: 10 | 11 | - Show compilation warnings and errors 12 | - Code lenses 13 | - Hover support 14 | 15 | ## Running the extension 16 | 17 | - Run `npm ci` in this folder. 18 | - Open VSCode inside this folder. 19 | - Switch to the "Run and Debug" pane. 20 | - Select "Launch Extension" from the drop down. 21 | - Press the ▶️ button next to "Launch Extension", which will start a TypeScript watcher and launch a new VSCode instance titled `[Extension Development Host]`. 22 | - The Grain CLI needs to be on your path. If not, set the `cliPath` setting inside the `[Extension Development Host]` instance to the location of your Grain CLI. 23 | - In the `[Extension Development Host]` instance of VSCode, open a document in 'grain' language mode. 24 | - After a code change, you'll likely need to trigger a "Developer: Reload Window" action in the `[Extension Development Host]` instance before the extension will pick up the changes. 25 | 26 | ## Useful files 27 | 28 | - `package.json` - this is the manifest file in which you declare your language support and define 29 | the location of the grammar file that has been copied into your extension. 30 | - `syntaxes/grain.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization. 31 | - `language-configuration.json` - this the language configuration, defining the tokens that are used for 32 | comments and brackets. 33 | -------------------------------------------------------------------------------- /editor-extensions/vscode/grain_shorthand_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grain-lang/grain-language-server/a02dd6336bf237af1ee6fda4d3c606586214e182/editor-extensions/vscode/grain_shorthand_white.png -------------------------------------------------------------------------------- /editor-extensions/vscode/icons/grain_shorthand_color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /editor-extensions/vscode/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "blockComment": ["/*", "*/"] 5 | }, 6 | // symbols used as brackets 7 | "brackets": [ 8 | ["{", "}"], 9 | ["[", "]"], 10 | ["(", ")"] 11 | ], 12 | // symbols that are auto closed when typing 13 | "autoClosingPairs": [ 14 | ["{", "}"], 15 | ["[", "]"], 16 | ["(", ")"], 17 | ["\"", "\""], 18 | ["'", "'"], 19 | ["/*", "*/"] 20 | ], 21 | // symbols that that can be used to surround a selection 22 | "surroundingPairs": [ 23 | ["{", "}"], 24 | ["[", "]"], 25 | ["(", ")"], 26 | ["\"", "\""], 27 | ["'", "'"] 28 | ], 29 | // Based on https://github.com/kevb34ns/auto-comment-blocks/blob/24f8ebc584e3e77d6a116a5a03a80c55008b44a3/src/rules.ts#L7-L40 30 | "onEnterRules": [ 31 | { 32 | // e.g. /** | */ 33 | "beforeText": "^\\s*\\/\\*\\*(?!\\/)([^\\*]|\\*(?!\\/))*$", 34 | "afterText": "^\\s*\\*\\/$", 35 | "action": { "indent": "indentOutdent", "appendText": " * " } 36 | }, 37 | { 38 | // e.g. /** ...| 39 | "beforeText": "^\\s*\\/\\*\\*(?!\\/)([^\\*]|\\*(?!\\/))*$", 40 | "action": { "indent": "none", "appendText": " * " } 41 | }, 42 | { 43 | // e.g. * ...| 44 | "beforeText": "^(\\t|(\\ ))*\\ \\*(\\ ([^\\*]|\\*(?!\\/))*)?$", 45 | "action": { "indent": "none", "appendText": "* " } 46 | }, 47 | { 48 | // e.g. */| 49 | "beforeText": "^(\\t|(\\ ))*\\ \\*\\/\\s*$", 50 | "action": { "indent": "none", "removeText": 1 } 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /editor-extensions/vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-grain", 3 | "displayName": "Grain", 4 | "description": "Grain support for Visual Studio Code.", 5 | "version": "0.20.0", 6 | "publisher": "grain-lang", 7 | "keywords": [ 8 | "grain", 9 | "grain-lang" 10 | ], 11 | "icon": "grain_shorthand_white.png", 12 | "engines": { 13 | "vscode": "^1.67.0" 14 | }, 15 | "license": "MIT", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/grain-lang/grain-language-server" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/grain-lang/grain-language-server/issues" 22 | }, 23 | "categories": [ 24 | "Programming Languages" 25 | ], 26 | "activationEvents": [ 27 | "onLanguage:grain" 28 | ], 29 | "main": "./out/extension", 30 | "contributes": { 31 | "commands": [ 32 | { 33 | "command": "grain.restart", 34 | "title": "Grain: Restart Language Server" 35 | }, 36 | { 37 | "command": "grain.openOutput", 38 | "title": "Grain: Show Extension Output" 39 | } 40 | ], 41 | "languages": [ 42 | { 43 | "id": "grain", 44 | "aliases": [ 45 | "Grain", 46 | "grain" 47 | ], 48 | "extensions": [ 49 | ".gr" 50 | ], 51 | "icon": { 52 | "light": "./icons/grain_shorthand_color.svg", 53 | "dark": "./icons/grain_shorthand_color.svg" 54 | }, 55 | "configuration": "./language-configuration.json" 56 | }, 57 | { 58 | "id": "grain-type", 59 | "aliases": [ 60 | "Grain Type" 61 | ], 62 | "configuration": "./language-configuration.json" 63 | } 64 | ], 65 | "grammars": [ 66 | { 67 | "language": "grain", 68 | "scopeName": "source.grain", 69 | "path": "./syntaxes/grain.json" 70 | }, 71 | { 72 | "language": "grain-type", 73 | "scopeName": "source.grain-type", 74 | "path": "./syntaxes/grain-type.json" 75 | } 76 | ], 77 | "configurationDefaults": { 78 | "[grain]": { 79 | "files.insertFinalNewline": true 80 | } 81 | }, 82 | "configuration": { 83 | "type": "object", 84 | "title": "Grain Language Server configuration", 85 | "properties": { 86 | "grain.cliFlags": { 87 | "scope": "resource", 88 | "type": "string", 89 | "description": "Space-separated list of flags to pass to the grain CLI" 90 | }, 91 | "grain.cliPath": { 92 | "scope": "resource", 93 | "type": "string", 94 | "description": "Absolute path to the grain CLI (detected in PATH if not specified)" 95 | }, 96 | "grain.enableLSP": { 97 | "scope": "resource", 98 | "type": "boolean", 99 | "default": true, 100 | "description": "Enable the language server" 101 | }, 102 | "grain.trace.server": { 103 | "scope": "resource", 104 | "type": "string", 105 | "enum": [ 106 | "off", 107 | "messages", 108 | "verbose" 109 | ], 110 | "default": "off", 111 | "description": "Traces the communication between VS Code and the language server." 112 | } 113 | } 114 | } 115 | }, 116 | "scripts": { 117 | "vscode:prepublish": "npm run build-type-grammar && npm run esbuild-base -- --minify", 118 | "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node", 119 | "esbuild": "npm run esbuild-base -- --sourcemap", 120 | "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch", 121 | "build-type-grammar": "node ./syntaxes/generate-grain-type.js", 122 | "test": "sh ./scripts/e2e.sh", 123 | "package": "vsce package", 124 | "test-compile": "tsc -p ./", 125 | "format": "prettier --write .", 126 | "check-format": "prettier --check .", 127 | "deploy": "vsce publish && ovsx publish" 128 | }, 129 | "dependencies": { 130 | "vscode-languageclient": "^8.0.2-next.4", 131 | "which": "^2.0.2" 132 | }, 133 | "devDependencies": { 134 | "@types/mocha": "^9.1.1", 135 | "@types/node": "^12.12.0", 136 | "@types/vscode": "1.67.0", 137 | "@types/which": "^2.0.1", 138 | "@typescript-eslint/parser": "^5.25.0", 139 | "@vscode/test-electron": "^2.1.3", 140 | "esbuild": "^0.14.42", 141 | "eslint": "^8.16.0", 142 | "mocha": "^9.1.2", 143 | "ovsx": "^0.3.0", 144 | "prettier": "^2.6.2", 145 | "typescript": "^4.6.4", 146 | "vsce": "^2.9.0" 147 | }, 148 | "__metadata": { 149 | "id": "b95eb54f-dbaf-4bb5-a18c-8b56da8a0829", 150 | "publisherDisplayName": "The Grain Programming Language", 151 | "publisherId": "28fcb3bd-39d4-48a2-9e5b-115ceb2c067b" 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /editor-extensions/vscode/scripts/e2e.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export CODE_TESTS_PATH="$(pwd)/out/test" 4 | export CODE_TESTS_WORKSPACE="$(pwd)/testFixture" 5 | 6 | #node "$(pwd)/out/test/runTest" 7 | -------------------------------------------------------------------------------- /editor-extensions/vscode/src/GrainDocCompletionProvider.ts: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/microsoft/vscode/blob/94c9ea46838a9a619aeafb7e8afd1170c967bb55/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts 2 | import * as vscode from "vscode"; 3 | import { LanguageClient } from "vscode-languageclient/node"; 4 | 5 | const defaultGrainDoc = new vscode.SnippetString(`/**\n * $0\n */`); 6 | 7 | class GrainDocCompletionItem extends vscode.CompletionItem { 8 | constructor( 9 | public readonly document: vscode.TextDocument, 10 | public readonly position: vscode.Position 11 | ) { 12 | super("/** */", vscode.CompletionItemKind.Text); 13 | this.detail = "Grain Doc Comment"; 14 | this.sortText = "\0"; 15 | 16 | const line = document.lineAt(position.line).text; 17 | const prefix = line.slice(0, position.character).match(/\/\**\s*$/); 18 | const suffix = line.slice(position.character).match(/^\s*\**\//); 19 | const start = position.translate(0, prefix ? -prefix[0].length : 0); 20 | const range = new vscode.Range( 21 | start, 22 | position.translate(0, suffix ? suffix[0].length : 0) 23 | ); 24 | this.range = { inserting: range, replacing: range }; 25 | } 26 | } 27 | 28 | export class GrainDocCompletionProvider 29 | implements vscode.CompletionItemProvider 30 | { 31 | constructor(readonly client: LanguageClient) {} 32 | 33 | public async provideCompletionItems( 34 | document: vscode.TextDocument, 35 | position: vscode.Position, 36 | token: vscode.CancellationToken 37 | ): Promise { 38 | if (!this.isPotentiallyValidDocCompletionPosition(document, position)) { 39 | return undefined; 40 | } 41 | 42 | const item = new GrainDocCompletionItem(document, position); 43 | 44 | // TODO: Get param and returns details from `grain lsp` 45 | item.insertText = defaultGrainDoc; 46 | 47 | return [item]; 48 | } 49 | 50 | private isPotentiallyValidDocCompletionPosition( 51 | document: vscode.TextDocument, 52 | position: vscode.Position 53 | ): boolean { 54 | // Only show the GrainDoc completion when the everything before the cursor is whitespace 55 | // or could be the opening of a comment 56 | const line = document.lineAt(position.line).text; 57 | const prefix = line.slice(0, position.character); 58 | if (!/^\s*$|\/\*\*\s*$|^\s*\/\*\*+\s*$/.test(prefix)) { 59 | return false; 60 | } 61 | 62 | // And everything after is possibly a closing comment or more whitespace 63 | const suffix = line.slice(position.character); 64 | return /^\s*(\*+\/)?\s*$/.test(suffix); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /editor-extensions/vscode/src/GrainErrorHandler.ts: -------------------------------------------------------------------------------- 1 | import { LanguageStatusSeverity, type LanguageStatusItem } from "vscode"; 2 | import { 3 | ErrorHandler, 4 | ErrorAction, 5 | CloseAction, 6 | type BaseLanguageClient, 7 | type ErrorHandlerResult, 8 | type Message, 9 | type CloseHandlerResult, 10 | } from "vscode-languageclient"; 11 | 12 | // Derived from: https://github.com/microsoft/vscode-languageserver-node/blob/a561f1342ba94ad7f550cb15446f65432f5e1367/client/src/common/client.ts#L439 13 | export default class GrainErrorHandler implements ErrorHandler { 14 | private readonly restarts: number[]; 15 | 16 | constructor( 17 | private name: string, 18 | private languageStatusItem: LanguageStatusItem, 19 | private maxRestartCount: number 20 | ) { 21 | this.restarts = []; 22 | } 23 | 24 | public error( 25 | _error: Error, 26 | _message: Message, 27 | count: number 28 | ): ErrorHandlerResult { 29 | if (count && count <= 3) { 30 | return { action: ErrorAction.Continue }; 31 | } 32 | return { action: ErrorAction.Shutdown }; 33 | } 34 | 35 | public closed(): CloseHandlerResult { 36 | this.restarts.push(Date.now()); 37 | if (this.restarts.length <= this.maxRestartCount) { 38 | return { action: CloseAction.Restart }; 39 | } else { 40 | const diff = this.restarts[this.restarts.length - 1] - this.restarts[0]; 41 | if (diff <= 3 * 60 * 1000) { 42 | this.languageStatusItem.severity = LanguageStatusSeverity.Error; 43 | return { 44 | action: CloseAction.DoNotRestart, 45 | message: `The ${this.name} server crashed ${ 46 | this.maxRestartCount + 1 47 | } times in the last 3 minutes. The server will not be restarted. See the output for more information.`, 48 | }; 49 | } else { 50 | this.restarts.shift(); 51 | return { action: CloseAction.Restart }; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /editor-extensions/vscode/src/extension.ts: -------------------------------------------------------------------------------- 1 | /* This file is largely copied from vscode's sample library. 2 | The original copyright notice is reproduced below. */ 3 | /* -------------------------------------------------------------------------------------------- 4 | * Copyright (c) Microsoft Corporation. All rights reserved. 5 | * Licensed under the MIT License. See License.txt in the project root for license information. 6 | * ------------------------------------------------------------------------------------------ */ 7 | 8 | import { 9 | workspace, 10 | commands, 11 | ExtensionContext, 12 | languages, 13 | Disposable, 14 | TextDocument, 15 | WorkspaceFolder, 16 | WorkspaceFoldersChangeEvent, 17 | ConfigurationChangeEvent, 18 | Uri, 19 | window, 20 | type LanguageStatusItem, 21 | } from "vscode"; 22 | 23 | import { 24 | LanguageClient, 25 | LanguageClientOptions, 26 | ServerOptions, 27 | } from "vscode-languageclient/node"; 28 | 29 | import which from "which"; 30 | 31 | import { GrainDocCompletionProvider } from "./GrainDocCompletionProvider"; 32 | import GrainErrorHandler from "./GrainErrorHandler"; 33 | 34 | let extensionName = "Grain Language Server"; 35 | 36 | let languageId = "grain"; 37 | let outputChannel = window.createOutputChannel(extensionName, languageId); 38 | let fileClients: Map = new Map(); 39 | let workspaceClients: Map = new Map(); 40 | 41 | let activeMutex: Set = new Set(); 42 | 43 | let grainStatusBarItem: LanguageStatusItem | null = null; 44 | let grainRestartStatusItem: LanguageStatusItem | null = null; 45 | 46 | function mutex(key: string, fn: (...args: unknown[]) => Promise) { 47 | return (...args) => { 48 | if (activeMutex.has(key)) return; 49 | 50 | activeMutex.add(key); 51 | 52 | fn(...args) 53 | .catch((err) => 54 | // Rethrow on a "next tick" to break out of the promise wrapper 55 | queueMicrotask(() => { 56 | throw err; 57 | }) 58 | ) 59 | .finally(() => { 60 | activeMutex.delete(key); 61 | }); 62 | }; 63 | } 64 | 65 | const grainBinaries = [ 66 | "grain", 67 | "grain-mac-x64", 68 | "grain-linux-x64", 69 | "grain-win-x64", 70 | ]; 71 | 72 | function findGrain() { 73 | for (const bin of grainBinaries) { 74 | try { 75 | const grain = which.sync(bin); 76 | // If it didn't throw, we found a grain binary 77 | return grain; 78 | } catch (err) { 79 | // Not found 80 | } 81 | } 82 | throw new Error("Unable to locate any Grain binary. Did you install it?"); 83 | } 84 | 85 | function dirpathFromUri(uri: Uri): string { 86 | let dirPath = uri.toString(); 87 | if (!dirPath.endsWith("/")) { 88 | return dirPath + "/"; 89 | } 90 | return dirPath; 91 | } 92 | 93 | function globFromUri(uri: Uri, glob: string) { 94 | // globs always need to use `/` 95 | return `${uri.fsPath}${glob}`.replaceAll("\\", "/"); 96 | } 97 | 98 | let workspaceFolders: string[] = []; 99 | function sortWorkspaceFolders(): string[] { 100 | if (workspaceFolders.length === 0) { 101 | if (!workspace.workspaceFolders) { 102 | workspaceFolders = []; 103 | } else { 104 | workspaceFolders = workspace.workspaceFolders 105 | .map((folder) => dirpathFromUri(folder.uri)) 106 | .sort((a, b) => a.length - b.length); 107 | } 108 | } 109 | return workspaceFolders; 110 | } 111 | 112 | function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder { 113 | let sorted = sortWorkspaceFolders(); 114 | for (let element of sorted) { 115 | let dirpath = dirpathFromUri(folder.uri); 116 | if (dirpath.startsWith(element)) { 117 | return workspace.getWorkspaceFolder(Uri.parse(element))!; 118 | } 119 | } 120 | return folder; 121 | } 122 | 123 | function getLspCommand(uri: Uri) { 124 | let config = workspace.getConfiguration("grain", uri); 125 | 126 | let lspEnabled = config.get("enableLSP"); 127 | 128 | if (!lspEnabled) { 129 | return; 130 | } 131 | 132 | let command = config.get("cliPath") || findGrain(); 133 | // For some reason, if you specify a capitalized EXE extension for our pkg binary, 134 | // it crashes the LSP so we just lowercase any .EXE ending in the command 135 | command = command.replace(/\.EXE$/, ".exe"); 136 | 137 | let flags = config.get("cliFlags") || ""; 138 | 139 | let args = ["lsp", ...flags.split(" ")]; 140 | 141 | return [command, args] as const; 142 | } 143 | 144 | async function startFileClient(uri: Uri) { 145 | let [command, args] = getLspCommand(uri); 146 | 147 | let clientOptions: LanguageClientOptions = { 148 | documentSelector: [ 149 | { 150 | scheme: uri.scheme, 151 | language: languageId, 152 | pattern: `${globFromUri(uri, "")}`, 153 | }, 154 | ], 155 | outputChannel, 156 | errorHandler: 157 | grainStatusBarItem != null 158 | ? new GrainErrorHandler(extensionName, grainStatusBarItem, 5) 159 | : undefined, 160 | }; 161 | 162 | let serverOptions: ServerOptions = { 163 | command, 164 | args, 165 | }; 166 | 167 | let client = new LanguageClient( 168 | languageId, 169 | extensionName, 170 | serverOptions, 171 | clientOptions 172 | ); 173 | 174 | client.info( 175 | `Starting LSP client using command: ${command} ${args.join(" ")}` 176 | ); 177 | 178 | await client.start(); 179 | 180 | let grainDocCompletionProvider = new GrainDocCompletionProvider(client); 181 | languages.registerCompletionItemProvider( 182 | "grain", 183 | grainDocCompletionProvider, 184 | "*" 185 | ); 186 | 187 | return client; 188 | } 189 | 190 | async function addFileClient(uri: Uri) { 191 | let file = uri.toString(); 192 | if (!fileClients.has(file)) { 193 | // Start the client. This will also launch the server 194 | let client = await startFileClient(uri); 195 | fileClients.set(file, client); 196 | } 197 | } 198 | 199 | async function removeFileClient(uri: Uri) { 200 | let file = uri.toString(); 201 | let client = fileClients.get(file); 202 | if (client) { 203 | await client.stop(); 204 | fileClients.delete(file); 205 | } 206 | } 207 | 208 | async function startWorkspaceClient(workspaceFolder: WorkspaceFolder) { 209 | let [command, args] = getLspCommand(workspaceFolder.uri); 210 | 211 | let clientOptions: LanguageClientOptions = { 212 | documentSelector: [ 213 | { 214 | scheme: "file", 215 | language: languageId, 216 | // Glob starts with `/` because it just appends both segments 217 | pattern: `${globFromUri(workspaceFolder.uri, "/**/*")}`, 218 | }, 219 | ], 220 | workspaceFolder, 221 | outputChannel, 222 | }; 223 | 224 | let serverOptions: ServerOptions = { 225 | command, 226 | args, 227 | }; 228 | 229 | let client = new LanguageClient( 230 | languageId, 231 | extensionName, 232 | serverOptions, 233 | clientOptions 234 | ); 235 | 236 | client.info( 237 | `Starting LSP client using command: ${command} ${args.join(" ")}` 238 | ); 239 | 240 | await client.start(); 241 | 242 | let grainDocCompletionProvider = new GrainDocCompletionProvider(client); 243 | languages.registerCompletionItemProvider( 244 | "grain", 245 | grainDocCompletionProvider, 246 | "*" 247 | ); 248 | 249 | return client; 250 | } 251 | 252 | async function addWorkspaceClient(workspaceFolder: WorkspaceFolder) { 253 | let workspacePath = workspaceFolder.uri.toString(); 254 | if (!workspaceClients.has(workspacePath)) { 255 | // Start the client. This will also launch the server 256 | let client = await startWorkspaceClient(workspaceFolder); 257 | workspaceClients.set(workspacePath, client); 258 | } 259 | } 260 | 261 | async function removeWorkspaceClient(workspaceFolder: WorkspaceFolder) { 262 | let workspacePath = workspaceFolder.uri.toString(); 263 | let client = workspaceClients.get(workspacePath); 264 | if (client) { 265 | await client.stop(); 266 | workspaceClients.delete(workspacePath); 267 | } 268 | } 269 | 270 | async function restartAllClients() { 271 | if (grainRestartStatusItem != null) grainRestartStatusItem.busy = true; 272 | for (let client of fileClients.values()) { 273 | await client.restart(); 274 | } 275 | for (let client of workspaceClients.values()) { 276 | await client.restart(); 277 | } 278 | if (grainRestartStatusItem != null) grainRestartStatusItem.busy = false; 279 | } 280 | 281 | async function didOpenTextDocument( 282 | document: TextDocument 283 | ): Promise { 284 | // We are only interested in language mode text 285 | if (document.languageId !== languageId) { 286 | return Disposable.from(); 287 | } 288 | 289 | let uri = document.uri; 290 | let folder = workspace.getWorkspaceFolder(uri); 291 | let configHandler; 292 | if (folder) { 293 | // If we have nested workspace folders we only start a server on the outer most workspace folder. 294 | folder = getOuterMostWorkspaceFolder(folder); 295 | 296 | await addWorkspaceClient(folder); 297 | 298 | configHandler = mutex( 299 | folder.uri.toString(), 300 | async (e: ConfigurationChangeEvent) => { 301 | if (e.affectsConfiguration("grain.cliFlags", folder.uri)) { 302 | await removeWorkspaceClient(folder); 303 | await addWorkspaceClient(folder); 304 | } 305 | 306 | if (e.affectsConfiguration("grain.cliPath", folder.uri)) { 307 | await removeWorkspaceClient(folder); 308 | await addWorkspaceClient(folder); 309 | } 310 | 311 | if (e.affectsConfiguration("grain.enableLSP", folder.uri)) { 312 | await removeWorkspaceClient(folder); 313 | await addWorkspaceClient(folder); 314 | } 315 | } 316 | ); 317 | } else { 318 | // We only want to handle `file:` and `untitled:` schemes because 319 | //vscode sends `output:` schemes for markdown responses from our LSP 320 | if (uri.scheme !== "file" && uri.scheme !== "untitled") { 321 | return Disposable.from(); 322 | } 323 | 324 | // Each file outside of a workspace gets it's own client 325 | await addFileClient(uri); 326 | 327 | configHandler = mutex( 328 | uri.toString(), 329 | async (e: ConfigurationChangeEvent) => { 330 | if (e.affectsConfiguration("grain.cliFlags", uri)) { 331 | await removeFileClient(uri); 332 | await addFileClient(uri); 333 | } 334 | 335 | if (e.affectsConfiguration("grain.cliPath", uri)) { 336 | await removeFileClient(uri); 337 | await addFileClient(uri); 338 | } 339 | 340 | if (e.affectsConfiguration("grain.enableLSP", uri)) { 341 | await removeFileClient(uri); 342 | await addFileClient(uri); 343 | } 344 | } 345 | ); 346 | } 347 | 348 | return workspace.onDidChangeConfiguration(configHandler); 349 | } 350 | 351 | async function didChangeWorkspaceFolders(event: WorkspaceFoldersChangeEvent) { 352 | // Reset the workspace folders so it'll sort them again 353 | workspaceFolders = []; 354 | 355 | // Do nothing for newly added workspaces because their LSP will be booted 356 | // up when a file is opened 357 | 358 | // Remove any clients for workspaces that were closed 359 | for (let folder of event.removed) { 360 | await removeWorkspaceClient(folder); 361 | } 362 | } 363 | 364 | function createStatusBarEntries(context: ExtensionContext) { 365 | // Language status bar 366 | const statusScope = { language: languageId }; 367 | 368 | // Main extension entry 369 | grainStatusBarItem = languages.createLanguageStatusItem("grain", statusScope); 370 | grainStatusBarItem.busy = false; 371 | grainStatusBarItem.name = "grain"; 372 | grainStatusBarItem.text = "grain"; 373 | grainStatusBarItem.command = { 374 | title: "Open Grain Output", 375 | command: "grain.openOutput", 376 | }; 377 | context.subscriptions.push(grainStatusBarItem); 378 | 379 | // Restart command entry 380 | grainRestartStatusItem = languages.createLanguageStatusItem( 381 | "grain.restart", 382 | statusScope 383 | ); 384 | grainStatusBarItem.busy = false; 385 | grainRestartStatusItem.name = "grain.restart"; 386 | grainRestartStatusItem.text = "grain"; 387 | grainRestartStatusItem.command = { 388 | title: "Restart Extension", 389 | command: "grain.restart", 390 | }; 391 | context.subscriptions.push(grainRestartStatusItem); 392 | } 393 | 394 | export async function activate(context: ExtensionContext): Promise { 395 | const didOpenTextDocument$ = 396 | workspace.onDidOpenTextDocument(didOpenTextDocument); 397 | const didChangeWorkspaceFolders$ = workspace.onDidChangeWorkspaceFolders( 398 | didChangeWorkspaceFolders 399 | ); 400 | const restart$ = commands.registerCommand("grain.restart", restartAllClients); 401 | const output$ = commands.registerCommand("grain.openOutput", () => { 402 | outputChannel.show(); 403 | }); 404 | 405 | context.subscriptions.push( 406 | didOpenTextDocument$, 407 | didChangeWorkspaceFolders$, 408 | restart$, 409 | output$ 410 | ); 411 | createStatusBarEntries(context); 412 | 413 | for (let doc of workspace.textDocuments) { 414 | const disposable = await didOpenTextDocument(doc); 415 | context.subscriptions.push(disposable); 416 | } 417 | } 418 | 419 | export async function deactivate(): Promise { 420 | for (let client of fileClients.values()) { 421 | await client.stop(); 422 | } 423 | for (let client of workspaceClients.values()) { 424 | await client.stop(); 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /editor-extensions/vscode/syntaxes/generate-grain-type.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const grain = require("./grain.json"); 4 | 5 | const grainType = { 6 | ...grain, 7 | name: "Grain Type", 8 | patterns: [ 9 | { 10 | include: "#data-declarations", 11 | }, 12 | { 13 | include: "#type", 14 | }, 15 | ], 16 | scopeName: "source.grain-type", 17 | }; 18 | 19 | fs.writeFileSync( 20 | path.join(__dirname, "grain-type.json"), 21 | JSON.stringify(grainType) 22 | ); 23 | -------------------------------------------------------------------------------- /editor-extensions/vscode/syntaxes/grain.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "Grain", 4 | "patterns": [ 5 | { 6 | "include": "#top-level-statements" 7 | } 8 | ], 9 | "repository": { 10 | "modules": { 11 | "patterns": [ 12 | { 13 | "begin": "\\b(module)\\b\\s+([A-Z]\\w*)\\s*(\\{)", 14 | "end": "(\\})", 15 | "beginCaptures": { 16 | "1": { "name": "storage.type.grain" }, 17 | "2": { "name": "entity.name.type.grain" } 18 | }, 19 | "endCaptures": { 20 | "1": { "name": "punctuation.definition.parameters.end.grain" } 21 | }, 22 | "patterns": [{ "include": "#top-level-statements" }] 23 | }, 24 | { 25 | "match": "\\b(module)\\b\\s+([A-Z]\\w*)", 26 | "captures": { 27 | "1": { "name": "storage.type.grain" }, 28 | "2": { "name": "entity.name.type.grain" } 29 | } 30 | } 31 | ] 32 | }, 33 | "bindings": { 34 | "patterns": [ 35 | { 36 | "match": "\\b((?:let)\\b\\s*(?:rec\\b)?\\s*(?:mut\\b)?)\\b\\s*(?!rec|mut)([a-z]\\w*)\\s*(:.*?)?(=)(?=.*=>)", 37 | "captures": { 38 | "1": { 39 | "name": "storage.type.grain" 40 | }, 41 | "2": { 42 | "name": "entity.name.function.grain" 43 | }, 44 | "3": { 45 | "patterns": [{ "include": "#type-annotations" }] 46 | }, 47 | "4": { 48 | "name": "keyword.operator.grain" 49 | } 50 | } 51 | }, 52 | { 53 | "match": "\\b((?:let)\\b\\s*(?:rec\\b)?\\s*(?:mut\\b)?)\\b\\s*(?!rec|mut)([a-z]\\w*)\\s*(:.*?)?(=)(?=\\s*pattern\\b)", 54 | "captures": { 55 | "1": { 56 | "name": "storage.type.grain" 57 | }, 58 | "2": { 59 | "name": "entity.name.function.grain" 60 | }, 61 | "3": { 62 | "patterns": [ 63 | { 64 | "include": "#type-annotations" 65 | } 66 | ] 67 | }, 68 | "4": { 69 | "name": "keyword.operator.grain" 70 | } 71 | } 72 | }, 73 | { 74 | "begin": "\\b((?:let)\\b\\s*(?:rec\\b)?\\s*(?:mut\\b)?)\\b\\s*(\\()", 75 | "end": "(\\))\\s*(:.*?)?(=)", 76 | "beginCaptures": { 77 | "1": { "name": "storage.type.grain" }, 78 | "2": { "name": "punctuation.definition.parameters.start.grain" } 79 | }, 80 | "endCaptures": { 81 | "1": { "name": "punctuation.definition.parameters.end.grain" }, 82 | "2": { 83 | "patterns": [ 84 | { 85 | "include": "#type-annotations" 86 | } 87 | ] 88 | }, 89 | "3": { "name": "keyword.operator.grain" } 90 | }, 91 | "patterns": [{ "include": "#binding-destructure" }] 92 | }, 93 | { 94 | "match": "\\b((?:let)\\b\\s*(?:rec\\b)?\\s*(?:mut\\b)?)\\b\\s*(?!rec|mut)([a-z]\\w*)\\s*(:.*?)?(=)", 95 | "captures": { 96 | "1": { 97 | "name": "storage.type.grain" 98 | }, 99 | "2": { 100 | "patterns": [ 101 | { 102 | "include": "#identifier" 103 | } 104 | ] 105 | }, 106 | "3": { 107 | "patterns": [ 108 | { 109 | "include": "#type-annotations" 110 | } 111 | ] 112 | }, 113 | "4": { 114 | "name": "keyword.operator.grain" 115 | } 116 | } 117 | }, 118 | { 119 | "match": "\\b((?:let)\\b\\s*(?:rec\\b)?\\s*(?:mut\\b)?)\\b\\s*(?!rec|mut)([a-z]\\w*)\\b\\s*(:.*)?", 120 | "captures": { 121 | "1": { 122 | "name": "storage.type.grain" 123 | }, 124 | "2": { 125 | "patterns": [ 126 | { 127 | "include": "#identifier" 128 | } 129 | ] 130 | }, 131 | "3": { 132 | "patterns": [{ "include": "#type-annotations" }] 133 | } 134 | } 135 | } 136 | ] 137 | }, 138 | "binding-destructure": { 139 | "patterns": [ 140 | { 141 | "begin": "(?=\\()", 142 | "end": "(?<=\\))", 143 | "patterns": [{ "include": "#binding-destructure" }] 144 | }, 145 | { "include": "#typed-identifier" } 146 | ] 147 | }, 148 | "comments": { 149 | "patterns": [ 150 | { 151 | "match": "\\/\\/.*$", 152 | "name": "comment.line.grain" 153 | }, 154 | { 155 | "match": "#!.*$", 156 | "name": "comment.line.grain" 157 | }, 158 | { 159 | "begin": "\\/\\*\\*", 160 | "end": "\\*\\/", 161 | "name": "comment.block.grain", 162 | "patterns": [ 163 | { 164 | "match": "((@)module)\\s+(\\b\\w+\\b)(:)", 165 | "captures": { 166 | "1": { "name": "storage.type.graindoc" }, 167 | "2": { "name": "punctuation.definition.block.tag.graindoc" }, 168 | "3": { "name": "entity.name.type.graindoc" }, 169 | "4": { "name": "punctuation.definition.block.tag.graindoc" } 170 | } 171 | }, 172 | { 173 | "match": "((@)example)\\s+(.*)", 174 | "captures": { 175 | "1": { "name": "storage.type.graindoc" }, 176 | "2": { "name": "punctuation.definition.block.tag.graindoc" }, 177 | "3": { "patterns": [{ "include": "#top-level-statements" }] } 178 | } 179 | }, 180 | { 181 | "match": "((@)since)\\s+\\b(v\\d+\\.\\d+\\.\\d+)\\b", 182 | "captures": { 183 | "1": { "name": "storage.type.graindoc" }, 184 | "2": { "name": "punctuation.definition.block.tag.graindoc" }, 185 | "3": { "name": "constant.numeric.graindoc" } 186 | } 187 | }, 188 | { 189 | "match": "((@)history)\\s+(v\\d+\\.\\d+\\.\\d+)(:)", 190 | "captures": { 191 | "1": { "name": "storage.type.graindoc" }, 192 | "2": { "name": "punctuation.definition.block.tag.graindoc" }, 193 | "3": { "name": "constant.numeric.graindoc" }, 194 | "4": { "name": "punctuation.definition.block.tag.graindoc" } 195 | } 196 | }, 197 | { 198 | "match": "((@)param)\\s+(\\b\\w+\\b)(:)", 199 | "captures": { 200 | "1": { "name": "storage.type.graindoc" }, 201 | "2": { "name": "punctuation.definition.block.tag.graindoc" }, 202 | "3": { "name": "variable.other.graindoc" }, 203 | "4": { "name": "punctuation.definition.block.tag.graindoc" } 204 | } 205 | }, 206 | { 207 | "match": "((@)section)\\s+(\\b\\w+\\b)(:)", 208 | "captures": { 209 | "1": { "name": "storage.type.graindoc" }, 210 | "2": { "name": "punctuation.definition.block.tag.graindoc" }, 211 | "3": { "name": "variable.other.graindoc" }, 212 | "4": { "name": "punctuation.definition.block.tag.graindoc" } 213 | } 214 | }, 215 | { 216 | "match": "((@)throws)\\s+(.*?)(:)", 217 | "captures": { 218 | "1": { "name": "storage.type.graindoc" }, 219 | "2": { "name": "punctuation.definition.block.tag.graindoc" }, 220 | "3": { 221 | "patterns": [{ "include": "#type-variant" }] 222 | }, 223 | "4": { "name": "punctuation.definition.block.tag.graindoc" } 224 | } 225 | }, 226 | { 227 | "match": "((@)(param|returns|module|example|section|deprecated|since|history|throws))", 228 | "captures": { 229 | "1": { "name": "storage.type.graindoc" }, 230 | "2": { "name": "punctuation.definition.block.tag.graindoc" } 231 | } 232 | } 233 | ] 234 | }, 235 | { 236 | "begin": "\\/\\*", 237 | "end": "\\*\\/", 238 | "name": "comment.block.grain" 239 | } 240 | ] 241 | }, 242 | "type-args": { 243 | "patterns": [ 244 | { 245 | "match": "(\\b[a-z]\\w*\\b)(,)?", 246 | "captures": { 247 | "1": { "name": "variable.language.grain" }, 248 | "2": { "name": "punctuation.definition.parameters.begin.grain" } 249 | } 250 | } 251 | ] 252 | }, 253 | "type-constructor": { 254 | "patterns": [ 255 | { 256 | "begin": "(\\()", 257 | "end": "(\\))", 258 | "captures": { 259 | "1": { "name": "punctuation.definition.parameters.grain" } 260 | }, 261 | "patterns": [ 262 | { 263 | "match": ",", 264 | "name": "punctuation.definition.parameters.grain" 265 | }, 266 | { "include": "#type-constructor" } 267 | ] 268 | }, 269 | { 270 | "begin": "(\\b[A-Z]\\w*\\b)\\s*(<)", 271 | "end": "((?)", 272 | "beginCaptures": { 273 | "1": { 274 | "name": "entity.name.type.grain" 275 | }, 276 | "2": { "name": "punctuation.definition.parameters.grain" } 277 | }, 278 | "endCaptures": { 279 | "1": { "name": "punctuation.definition.parameters.grain" } 280 | }, 281 | "patterns": [ 282 | { "include": "#type" }, 283 | { 284 | "match": ",", 285 | "name": "punctuation.definition.parameters.grain" 286 | } 287 | ] 288 | }, 289 | { 290 | "match": "(\\b[A-Z]\\w*\\b)", 291 | "captures": { 292 | "1": { 293 | "name": "entity.name.type.grain" 294 | } 295 | } 296 | } 297 | ] 298 | }, 299 | "type": { 300 | "patterns": [ 301 | { 302 | "begin": "(\\()", 303 | "end": "(\\))", 304 | "beginCaptures": { 305 | "1": { "name": "punctuation.definition.parameters.begin.grain" } 306 | }, 307 | "endCaptures": { 308 | "1": { "name": "punctuation.definition.parameters.end.grain" } 309 | }, 310 | "patterns": [ 311 | { 312 | "match": "(,)", 313 | "captures": { 314 | "1": { 315 | "name": "punctuation.definition.parameters.grain" 316 | } 317 | } 318 | }, 319 | { "include": "#type" } 320 | ] 321 | }, 322 | { 323 | "match": "(->|\\.)", 324 | "name": "keyword.operator.grain" 325 | }, 326 | { "include": "#type-args" }, 327 | { "include": "#type-constructor" } 328 | ] 329 | }, 330 | "type-annotations": { 331 | "patterns": [ 332 | { 333 | "begin": "(:|->)\\s*(?=\\()", 334 | "end": "(?<=\\))", 335 | "captures": { 336 | "1": { "name": "keyword.operator.grain" } 337 | }, 338 | "patterns": [{ "include": "#type" }] 339 | }, 340 | { 341 | "match": "(->)\\s*(\\w+(\\.\\w+)*(<.*?(?)?)", 342 | "captures": { 343 | "1": { "name": "keyword.operator.grain" }, 344 | "2": { "patterns": [{ "include": "#type" }] } 345 | } 346 | }, 347 | { 348 | "match": "(:)\\s*(\\w+(\\.\\w+)*(<.*?(?)?\\s*->\\s*\\w+(<.*?(?)?)", 349 | "captures": { 350 | "1": { "name": "keyword.operator.grain" }, 351 | "2": { "patterns": [{ "include": "#type" }] } 352 | } 353 | }, 354 | { 355 | "match": "(:)\\s*(\\w+(\\.\\w+)*(<.*?(?)?\\s*->\\s*\\(.*\\))", 356 | "captures": { 357 | "1": { "name": "keyword.operator.grain" }, 358 | "2": { "patterns": [{ "include": "#type" }] } 359 | } 360 | }, 361 | { 362 | "match": "(:)\\s*(\\w+(\\.\\w+)*(<.*?(?)?)", 363 | "captures": { 364 | "1": { "name": "keyword.operator.grain" }, 365 | "2": { "patterns": [{ "include": "#type" }] } 366 | } 367 | }, 368 | { 369 | "match": "(:)", 370 | "captures": { 371 | "1": { "name": "keyword.operator.grain" } 372 | } 373 | } 374 | ] 375 | }, 376 | "data-declarations": { 377 | "patterns": [ 378 | { 379 | "match": "\\b(type)\\b\\s+([A-Z]\\w*\\s*(<.*>\\s*)?)\\s*(=)\\s*(.*)", 380 | "captures": { 381 | "1": { "name": "storage.type.grain" }, 382 | "2": { "name": "entity.name.type.grain" }, 383 | "3": { "patterns": [{ "include": "#type-vector" }] }, 384 | "4": { "name": "keyword.operator.grain" }, 385 | "5": { "patterns": [{ "include": "#type" }] } 386 | } 387 | }, 388 | { 389 | "begin": "\\b(enum)\\b\\s+(\\b(rec)\\b\\s+)?([A-Z]\\w*\\s*(<.*>\\s*)?)\\s*(\\{)", 390 | "end": "(\\})", 391 | "beginCaptures": { 392 | "1": { "name": "storage.type.grain" }, 393 | "3": { "name": "storage.type.grain" }, 394 | "4": { "name": "entity.name.type.grain" }, 395 | "5": { "patterns": [{ "include": "#type-vector" }] }, 396 | "6": { "name": "punctuation.definition.parameters.begin.grain" } 397 | }, 398 | "endCaptures": { 399 | "1": { "name": "punctuation.definition.parameters.end.grain" } 400 | }, 401 | "patterns": [ 402 | { "include": "#type-variant" }, 403 | { 404 | "match": ",", 405 | "name": "punctuation.definition.grain" 406 | } 407 | ] 408 | }, 409 | { 410 | "begin": "\\b(record)\\b\\s+(\\b(rec)\\b\\s+)?([A-Z]\\w*\\s*(<.*>\\s*)?)\\s*(\\{)", 411 | "end": "(\\})", 412 | "beginCaptures": { 413 | "1": { "name": "storage.type.grain" }, 414 | "3": { "name": "storage.type.grain" }, 415 | "4": { "name": "entity.name.type.grain" }, 416 | "5": { "patterns": [{ "include": "#type-vector" }] }, 417 | "6": { "name": "punctuation.definition.parameters.begin.grain" } 418 | }, 419 | "endCaptures": { 420 | "1": { "name": "punctuation.definition.parameters.end.grain" } 421 | }, 422 | "patterns": [ 423 | { 424 | "match": "(mut\\b\\s*)?(\\w+)(?=\\s*:)", 425 | "captures": { 426 | "1": { "name": "storage.type.grain" }, 427 | "2": { "name": "entity.other.grain" } 428 | } 429 | }, 430 | { 431 | "include": "#type-annotations" 432 | }, 433 | { 434 | "match": ",", 435 | "name": "punctuation.definition.grain" 436 | }, 437 | { 438 | "include": "#comments" 439 | }, 440 | { 441 | "match": "mut", 442 | "name": "storage.type.grain" 443 | } 444 | ] 445 | }, 446 | { 447 | "match": "\\b(exception)\\b\\s+(.*)", 448 | "captures": { 449 | "1": { "patterns": [{ "include": "#keywords" }] }, 450 | "2": { "patterns": [{ "include": "#type-variant" }] } 451 | } 452 | }, 453 | { 454 | "match": "(type|record|enum)\\s+(\\b(rec)\\b\\s+)?([A-Z]\\w*)(<.*>)?", 455 | "captures": { 456 | "1": { "name": "storage.type.grain" }, 457 | "3": { "name": "storage.type.grain" }, 458 | "4": { "name": "entity.name.type.grain" }, 459 | "5": { "patterns": [{ "include": "#type-vector" }] } 460 | } 461 | } 462 | ] 463 | }, 464 | "statement": { 465 | "patterns": [ 466 | { 467 | "begin": "\\b(if|while|for)\\b\\s*(\\()", 468 | "end": "(\\))", 469 | "beginCaptures": { 470 | "1": { "name": "keyword.control.grain" }, 471 | "2": { "name": "punctuation.description.parameters.start.grain" } 472 | }, 473 | "endCaptures": { 474 | "1": { "name": "punctuation.description.parameters.end.grain" } 475 | }, 476 | "patterns": [ 477 | { 478 | "match": "(,|;)", 479 | "name": "punctuation.support.grain" 480 | }, 481 | { "include": "#expressions" } 482 | ] 483 | }, 484 | { 485 | "match": "\\b(if|else|while|for)\\b", 486 | "name": "keyword.control.grain" 487 | } 488 | ] 489 | }, 490 | "function-call": { 491 | "patterns": [ 492 | { 493 | "begin": "\\b([a-z]\\w*)\\b\\s*(\\()", 494 | "end": "(\\))", 495 | "beginCaptures": { 496 | "1": { "name": "entity.name.function.grain" }, 497 | "2": { "name": "punctuation.description.parameters.start.grain" } 498 | }, 499 | "endCaptures": { 500 | "1": { "name": "punctuation.description.parameters.end.grain" } 501 | }, 502 | "patterns": [ 503 | { 504 | "begin": "(?<=\\()", 505 | "end": "(?=\\)|,)", 506 | "patterns": [{ "include": "#expressions" }] 507 | }, 508 | { 509 | "begin": "(?<=,)", 510 | "end": "(?=\\)|,)", 511 | "patterns": [{ "include": "#expressions" }] 512 | }, 513 | { 514 | "match": ",", 515 | "name": "punctuation.support.grain" 516 | } 517 | ] 518 | } 519 | ] 520 | }, 521 | "function-body": { 522 | "patterns": [ 523 | { 524 | "begin": "(=>)\\s*(\\{)", 525 | "end": "(\\})", 526 | "beginCaptures": { 527 | "1": { "name": "keyword.operator.grain" }, 528 | "2": { "name": "punctuation.definition.parameters.begin.grain" } 529 | }, 530 | "endCaptures": { 531 | "1": { "name": "punctuation.definition.parameters.end.grain" } 532 | }, 533 | "patterns": [{ "include": "#expressions" }] 534 | }, 535 | { 536 | "begin": "(=>)\\s*(?=\\()", 537 | "end": "(?<=\\))", 538 | "captures": { 539 | "1": { "name": "keyword.operator.grain" } 540 | }, 541 | "patterns": [{ "include": "#expressions" }] 542 | }, 543 | { 544 | "match": "(=>)(.*?)", 545 | "captures": { 546 | "1": { "name": "keyword.operator.grain" }, 547 | "2": { "patterns": [{ "include": "#expressions" }] } 548 | } 549 | } 550 | ] 551 | }, 552 | "block-body": { 553 | "patterns": [ 554 | { 555 | "begin": "(?<=\\))\\s*(\\{)", 556 | "end": "(\\})", 557 | "beginCaptures": { 558 | "1": { "name": "punctuation.definition.parameters.begin.grain" } 559 | }, 560 | "endCaptures": { 561 | "1": { "name": "punctuation.definition.parameters.end.grain" } 562 | }, 563 | "patterns": [{ "include": "#expressions" }] 564 | }, 565 | { 566 | "begin": "(?<=else)\\s*(\\{)", 567 | "end": "(\\})", 568 | "beginCaptures": { 569 | "1": { "name": "punctuation.definition.parameters.begin.grain" } 570 | }, 571 | "endCaptures": { 572 | "1": { "name": "punctuation.definition.parameters.end.grain" } 573 | }, 574 | "patterns": [{ "include": "#expressions" }] 575 | } 576 | ] 577 | }, 578 | "records": { 579 | "patterns": [ 580 | { 581 | "begin": "(\\{)", 582 | "end": "(\\})", 583 | "captures": { 584 | "1": { "name": "punctuation.description.grain" } 585 | }, 586 | "patterns": [ 587 | { 588 | "begin": "\\b(\\w+)\\b\\s*(:)", 589 | "end": "(?=(,|\\}|#))", 590 | "beginCaptures": { 591 | "1": { "name": "entity.name.grain" }, 592 | "2": { "name": "punctuation.description.grain" } 593 | }, 594 | "patterns": [{ "include": "#expressions" }] 595 | }, 596 | { 597 | "match": "\\b(\\w+)\\b", 598 | "name": "entity.name.grain" 599 | }, 600 | { 601 | "match": ",", 602 | "name": "punctuation.description.parameters.grain" 603 | }, 604 | { 605 | "include": "#comments" 606 | } 607 | ] 608 | } 609 | ] 610 | }, 611 | "lists": { 612 | "patterns": [ 613 | { 614 | "begin": "(\\[)", 615 | "end": "(\\])", 616 | "captures": { 617 | "1": { "name": "punctuation.description.grain" } 618 | }, 619 | "patterns": [ 620 | { "include": "#expressions" }, 621 | { 622 | "match": ",", 623 | "name": "punctuation.description.parameters.grain" 624 | } 625 | ] 626 | } 627 | ] 628 | }, 629 | "arrays": { 630 | "patterns": [ 631 | { 632 | "begin": "(\\[>)", 633 | "end": "(\\])", 634 | "captures": { 635 | "1": { "name": "punctuation.description.grain" } 636 | }, 637 | "patterns": [ 638 | { "include": "#expressions" }, 639 | { 640 | "match": ",", 641 | "name": "punctuation.description.parameters.grain" 642 | } 643 | ] 644 | } 645 | ] 646 | }, 647 | "expressions": { 648 | "patterns": [ 649 | { "include": "#comments" }, 650 | { "include": "#bindings" }, 651 | { "include": "#use" }, 652 | { "include": "#function-body" }, 653 | { "include": "#block-body" }, 654 | { "include": "#tuple" }, 655 | { "include": "#records" }, 656 | { "include": "#arrays" }, 657 | { "include": "#lists" }, 658 | { "include": "#match" }, 659 | { "include": "#pattern" }, 660 | { "include": "#statement" }, 661 | { "include": "#function-call" }, 662 | { "include": "#keywords" }, 663 | { "include": "#constant" }, 664 | { "include": "#type-annotations" }, 665 | { "include": "#external-identifier" }, 666 | { "include": "#identifier" }, 667 | { "include": "#type-variant-constructor" } 668 | ] 669 | }, 670 | "use": { 671 | "patterns": [ 672 | { 673 | "begin": "\\b(use)\\b\\s*\\b([A-Z]\\w*((\\.)[A-Z]\\w*)*)\\b\\s*(\\.)\\s*(\\{)", 674 | "end": "(\\})", 675 | "beginCaptures": { 676 | "1": { "patterns": [{ "include": "#keywords" }] }, 677 | "2": { "name": "entity.name.module.grain" }, 678 | "4": { 679 | "name": "keyword.operator.accessor.grain" 680 | }, 681 | "5": { 682 | "name": "keyword.operator.accessor.grain" 683 | }, 684 | "6": { "name": "punctuation.definition.start.grain" } 685 | }, 686 | "endCaptures": { 687 | "1": { "name": "punctuation.definition.end.grain" } 688 | }, 689 | "patterns": [ 690 | { 691 | "match": "(\\*|\\,)", 692 | "name": "punctuation.support.grain" 693 | }, 694 | { 695 | "match": "\\b(type|exception)\\b\\s*([A-Z]\\w*)\\b", 696 | "captures": { 697 | "1": { "patterns": [{ "include": "#keywords" }] }, 698 | "2": { "patterns": [{ "include": "#type" }] } 699 | } 700 | }, 701 | { 702 | "include": "#keywords" 703 | }, 704 | { 705 | "match": "\\b[A-Z]\\w*\\b", 706 | "name": "entity.name.module.grain" 707 | }, 708 | { "include": "#identifier" } 709 | ] 710 | }, 711 | { 712 | "match": "\\b(use)\\b\\s*\\b([A-Z]\\w*((\\.)[A-Z]\\w*)*)\\b\\s*(\\.)\\s*(\\*)?", 713 | "captures": { 714 | "1": { "patterns": [{ "include": "#keywords" }] }, 715 | "2": { "name": "entity.name.module.grain" }, 716 | "4": { 717 | "name": "keyword.operator.accessor.grain" 718 | }, 719 | "5": { 720 | "name": "keyword.operator.accessor.grain" 721 | }, 722 | "6": { 723 | "name": "keyword.operator.grain" 724 | } 725 | } 726 | }, 727 | { 728 | "match": "\\b(use)\\b\\s*\\b([A-Z]\\w*((\\.)[A-Z]\\w*)*)\\b(\\.)?", 729 | "captures": { 730 | "1": { "patterns": [{ "include": "#keywords" }] }, 731 | "2": { "name": "entity.name.module.grain" }, 732 | "4": { 733 | "name": "keyword.operator.accessor.grain" 734 | }, 735 | "5": { 736 | "name": "keyword.operator.accessor.grain" 737 | } 738 | } 739 | } 740 | ] 741 | }, 742 | "operator": { 743 | "patterns": [ 744 | { 745 | "match": "(==|!=|<|>|\\^|\\+|-|\\*|%|/|&|\\||\\?\\?)[$&\\*/\\+\\-=><\\^\\|\\!\\?%:\\.]*", 746 | "name": "keyword.operator.grain" 747 | }, 748 | { 749 | "match": "(<=|>=|<|>|==|!=|:=|\\!)", 750 | "name": "keyword.operator.grain" 751 | }, 752 | { 753 | "match": "\\bis(nt)?\\b", 754 | "name": "keyword.operator.grain" 755 | }, 756 | { 757 | "begin": "(\\()", 758 | "end": "(\\))", 759 | "beginCaptures": { 760 | "1": { "name": "punctuation.definition.parameters.begin.grain" } 761 | }, 762 | "endCaptures": { 763 | "1": { "name": "punctuation.definition.parameters.end.grain" } 764 | }, 765 | "patterns": [{ "include": "#operator" }] 766 | } 767 | ] 768 | }, 769 | "identifier": { 770 | "patterns": [ 771 | { 772 | "include": "#operator" 773 | }, 774 | { 775 | "match": "(\\.\\.\\.|\\.)", 776 | "name": "keyword.operator.grain" 777 | }, 778 | { 779 | "match": "\\b_[A-Z0-9_]+\\b", 780 | "name": "variable.other.constant.grain" 781 | }, 782 | { 783 | "match": "\\b[a-z]\\w*\\b", 784 | "name": "variable.name.grain" 785 | } 786 | ] 787 | }, 788 | "external-identifier": { 789 | "patterns": [ 790 | { 791 | "match": "\\b([A-Z]\\w*)\\b(\\.)", 792 | "captures": { 793 | "1": { 794 | "name": "entity.name.module.grain" 795 | }, 796 | "2": { 797 | "name": "keyword.operator.accessor.grain" 798 | } 799 | } 800 | } 801 | ] 802 | }, 803 | "typed-identifier": { 804 | "patterns": [ 805 | { 806 | "match": "\\b([a-z]\\w*)\\b\\s*(:.*)(?=,)", 807 | "captures": { 808 | "1": { 809 | "patterns": [ 810 | { 811 | "include": "#identifier" 812 | } 813 | ] 814 | }, 815 | "2": { 816 | "patterns": [{ "include": "#type-annotations" }] 817 | } 818 | } 819 | }, 820 | { 821 | "match": "\\b([a-z]\\w*)\\b\\s*(:.*)(?=\\))", 822 | "captures": { 823 | "1": { 824 | "patterns": [ 825 | { 826 | "include": "#identifier" 827 | } 828 | ] 829 | }, 830 | "2": { 831 | "patterns": [{ "include": "#type-annotations" }] 832 | } 833 | } 834 | }, 835 | { "include": "#identifier" } 836 | ] 837 | }, 838 | "constant": { 839 | "patterns": [ 840 | { "include": "#number" }, 841 | { "include": "#strings" }, 842 | { "include": "#boolean" }, 843 | { "include": "#void" } 844 | ] 845 | }, 846 | "boolean": { 847 | "patterns": [ 848 | { 849 | "match": "\\b(true|false)\\b", 850 | "name": "constant.language.grain" 851 | } 852 | ] 853 | }, 854 | "void": { 855 | "patterns": [ 856 | { 857 | "match": "\\b(void)\\b", 858 | "name": "constant.language.grain" 859 | } 860 | ] 861 | }, 862 | "number": { 863 | "patterns": [ 864 | { 865 | "match": "\\b(\\d[\\d_]*)\\s*/\\s*(\\d[\\d_]*)(r?)\\b", 866 | "name": "constant.numeric.grain", 867 | "captures": { 868 | "3": { 869 | "name": "entity.name.type.numeric.grain" 870 | } 871 | } 872 | }, 873 | { 874 | "match": "\\b(\\d[\\d_]*)(\\.[\\d_]*)?([eE][+-]?\\d[\\d_]*)?([fdwW]?)\\b", 875 | "name": "constant.numeric.grain", 876 | "captures": { 877 | "4": { 878 | "name": "entity.name.type.numeric.grain" 879 | } 880 | } 881 | }, 882 | { 883 | "match": "\\b(0[xX][\\da-fA-F][\\da-fA-F_]*)(\\.[\\da-fA-F][\\da-fA-F_]*)?([pP][+-]?\\d[\\d_]*)?([fdwW]?)\\b", 884 | "name": "constant.numeric.grain", 885 | "captures": { 886 | "4": { 887 | "name": "entity.name.type.numeric.grain" 888 | } 889 | } 890 | }, 891 | { 892 | "match": "\\b(\\d[\\d_]*)(([sSlLnNt]|u[lLsS])?)\\b", 893 | "name": "constant.numeric.grain", 894 | "captures": { 895 | "2": { 896 | "name": "entity.name.type.numeric.grain" 897 | } 898 | } 899 | }, 900 | { 901 | "match": "\\b(0[xX][\\da-fA-F][\\da-fA-F_]*)(([sSlLnNt]|u[lLsS])?)\\b", 902 | "name": "constant.numeric.grain", 903 | "captures": { 904 | "3": { 905 | "name": "entity.name.type.numeric.grain" 906 | } 907 | } 908 | }, 909 | { 910 | "match": "\\b(0[oO][0-7][0-7_]*)(([sSlLnNt]|u[lLsS])?)\\b", 911 | "name": "constant.numeric.grain", 912 | "captures": { 913 | "3": { 914 | "name": "entity.name.type.numeric.grain" 915 | } 916 | } 917 | }, 918 | { 919 | "match": "\\b(0[bB][01][01_]*)(([sSlLnNt]|u[lLsS])?)\\b", 920 | "name": "constant.numeric.grain", 921 | "captures": { 922 | "3": { 923 | "name": "entity.name.type.numeric.grain" 924 | } 925 | } 926 | }, 927 | { 928 | "match": "\\b(Infinity)\\b", 929 | "name": "constant.numeric.grain" 930 | }, 931 | { 932 | "match": "\\b(NaN)\\b", 933 | "name": "constant.numeric.grain" 934 | } 935 | ] 936 | }, 937 | "tuple": { 938 | "patterns": [ 939 | { 940 | "begin": "(\\()", 941 | "end": "(\\))", 942 | "beginCaptures": { 943 | "1": { "name": "punctuation.definition.parameters.begin.grain" } 944 | }, 945 | "endCaptures": { 946 | "1": { "name": "punctuation.definition.parameters.end.grain" } 947 | }, 948 | "patterns": [ 949 | { 950 | "match": ",", 951 | "name": "punctuation.definition.parameters.grain" 952 | }, 953 | { "include": "#expressions" }, 954 | { "include": "#binding-destructure" } 955 | ] 956 | } 957 | ] 958 | }, 959 | "imports": { 960 | "patterns": [ 961 | { 962 | "begin": "(?<=import)", 963 | "end": "(?=from)", 964 | "patterns": [ 965 | { 966 | "match": "(\\*|\\,)", 967 | "name": "punctuation.support.grain" 968 | }, 969 | { 970 | "begin": "(\\{)", 971 | "end": "(\\})", 972 | "beginCaptures": { 973 | "1": { "name": "punctuation.definition.start.grain" } 974 | }, 975 | "endCaptures": { 976 | "1": { "name": "punctuation.definition.end.grain" } 977 | }, 978 | "patterns": [ 979 | { "match": ",", "name": "punctuation.definition.grain" }, 980 | { "include": "#expressions" } 981 | ] 982 | }, 983 | { 984 | "match": "\\b[A-Z]\\w*\\b", 985 | "name": "entity.name.module.grain" 986 | } 987 | ] 988 | }, 989 | { 990 | "match": "\\b(from)\\b\\s*(\\\".*\\\")\\s*\\b(include)\\b\\s*\\b([A-Z]\\w*)\\b(\\s*(as)\\s*\\b([A-Z]\\w*)\\b)?", 991 | "captures": { 992 | "1": { 993 | "patterns": [{ "include": "#keywords" }] 994 | }, 995 | "2": { 996 | "patterns": [{ "include": "#constant" }] 997 | }, 998 | "3": { 999 | "patterns": [{ "include": "#keywords" }] 1000 | }, 1001 | "4": { 1002 | "name": "entity.name.module.grain" 1003 | }, 1004 | "5": { 1005 | "patterns": [{ "include": "#keywords" }] 1006 | }, 1007 | "6": { 1008 | "name": "entity.name.module.grain" 1009 | } 1010 | } 1011 | } 1012 | ] 1013 | }, 1014 | "exports": { 1015 | "patterns": [ 1016 | { 1017 | "match": "\\b(export)\\b\\s*(\\*)\\s*\\b(except)\\b(.*?)(?=$|;)", 1018 | "captures": { 1019 | "1": { "name": "keyword.control.grain" }, 1020 | "2": { "name": "punctuation.support.grain" }, 1021 | "3": { "name": "keyword.control.grain" }, 1022 | "4": { 1023 | "patterns": [ 1024 | { 1025 | "include": "#expressions" 1026 | } 1027 | ] 1028 | } 1029 | } 1030 | }, 1031 | { 1032 | "match": "\\b(export)\\b\\s*(\\*)", 1033 | "captures": { 1034 | "1": { "name": "keyword.control.grain" }, 1035 | "2": { "name": "punctuation.support.grain" } 1036 | } 1037 | } 1038 | ] 1039 | }, 1040 | "foreigns": { 1041 | "patterns": [ 1042 | { 1043 | "begin": "\\b(import|export)\\s*\\b(foreign)\\b(\\s*\\bwasm\\b)\\s*", 1044 | "end": "\\b(from)\\b\\s+(.*)(?=$|;)", 1045 | "beginCaptures": { 1046 | "1": { "name": "keyword.control.grain" }, 1047 | "2": { "name": "keyword.control.grain" }, 1048 | "3": { "name": "variable.language.grain" } 1049 | }, 1050 | "endCaptures": { 1051 | "1": { "name": "keyword.control.grain" }, 1052 | "2": { "patterns": [{ "include": "#strings" }] } 1053 | }, 1054 | "patterns": [{ "include": "#expressions" }] 1055 | } 1056 | ] 1057 | }, 1058 | "primitives": { 1059 | "patterns": [ 1060 | { 1061 | "begin": "\\b(primitive)\\b", 1062 | "end": "(=)\\s*(('|\").*('|\"))$", 1063 | "beginCaptures": { 1064 | "1": { "name": "keyword.control.grain" } 1065 | }, 1066 | "endCaptures": { 1067 | "1": { "name": "keyword.control.grain" }, 1068 | "2": { 1069 | "patterns": [ 1070 | { 1071 | "include": "#strings" 1072 | } 1073 | ] 1074 | } 1075 | }, 1076 | "patterns": [ 1077 | { 1078 | "include": "#expressions" 1079 | } 1080 | ] 1081 | } 1082 | ] 1083 | }, 1084 | "strings": { 1085 | "patterns": [ 1086 | { 1087 | "name": "string.quoted.double.grain", 1088 | "begin": "\"", 1089 | "end": "\"", 1090 | "patterns": [ 1091 | { 1092 | "include": "#string-escape" 1093 | } 1094 | ] 1095 | }, 1096 | { 1097 | "name": "string.quoted.single.grain", 1098 | "begin": "'", 1099 | "end": "'", 1100 | "patterns": [ 1101 | { 1102 | "include": "#string-escape" 1103 | } 1104 | ] 1105 | } 1106 | ] 1107 | }, 1108 | "string-escape": { 1109 | "patterns": [ 1110 | { 1111 | "name": "constant.character.escape.grain", 1112 | "match": "(\\\\\\d{1,3}|\\\\x[A-Fa-f0-9]{1,2}|\\\\u[A-Fa-f0-9]{4}|\\\\u\\{[A-Fa-f0-9]{1,6}\\}|\\\\[bftvrn'\"\\\\])" 1113 | }, 1114 | { 1115 | "name": "invalid.illegal.grain", 1116 | "match": "\\\\." 1117 | } 1118 | ] 1119 | }, 1120 | "keywords": { 1121 | "patterns": [ 1122 | { 1123 | "match": "\\b(throw|exception|while|for|continue|break|return|match|when|pattern|assert|fail|import|export|from|include|use|provide|abstract|except|as)\\b", 1124 | "name": "keyword.control.grain" 1125 | }, 1126 | { 1127 | "match": "\\b(module|let|rec|mut|record|type|enum|and|foreign|primitive)\\b", 1128 | "name": "storage.type.grain" 1129 | } 1130 | ] 1131 | }, 1132 | "top-level-statements": { 1133 | "patterns": [ 1134 | { "include": "#modules" }, 1135 | { "include": "#foreigns" }, 1136 | { "include": "#imports" }, 1137 | { "include": "#exports" }, 1138 | { "include": "#primitives" }, 1139 | { "include": "#data-declarations" }, 1140 | { "include": "#expressions" }, 1141 | { "include": "#comments" }, 1142 | { 1143 | "match": "(;)", 1144 | "captures": { 1145 | "1": { "name": "punctuation.definition.parameters.grain" } 1146 | } 1147 | } 1148 | ] 1149 | }, 1150 | "program": { 1151 | "patterns": [{ "include": "#top-level-statements" }] 1152 | }, 1153 | "type-var": { 1154 | "patterns": [ 1155 | { 1156 | "match": "([a-z]\\w*)", 1157 | "captures": { 1158 | "1": { "name": "variable.language.grain" } 1159 | } 1160 | }, 1161 | { 1162 | "begin": "\\(", 1163 | "end": "\\)", 1164 | "patterns": [ 1165 | { 1166 | "include": "#type-var" 1167 | } 1168 | ] 1169 | } 1170 | ] 1171 | }, 1172 | "type-vector": { 1173 | "patterns": [ 1174 | { 1175 | "begin": "(<)", 1176 | "end": "(>)", 1177 | "captures": { 1178 | "1": { "name": "punctuation.definition.parameters.grain" } 1179 | }, 1180 | "patterns": [ 1181 | { "include": "#type-var" }, 1182 | { "include": "#type-constructor" }, 1183 | { 1184 | "match": ",", 1185 | "name": "punctuation.definition.parameters.grain" 1186 | } 1187 | ] 1188 | } 1189 | ] 1190 | }, 1191 | "type-variant": { 1192 | "patterns": [ 1193 | { "include": "#comments" }, 1194 | { 1195 | "begin": "\\b([A-Z]\\w*)\\b\\s*(\\()", 1196 | "end": "(\\))", 1197 | "beginCaptures": { 1198 | "1": { "patterns": [{ "include": "#type-variant-name" }] }, 1199 | "2": { "name": "punctuation.definition.parameters.begin.grain" } 1200 | }, 1201 | "endCaptures": { 1202 | "1": { "name": "punctuation.definition.parameters.end.grain" } 1203 | }, 1204 | "patterns": [ 1205 | { 1206 | "match": "(,)", 1207 | "captures": { 1208 | "1": { "name": "punctuation.definition.parameters.grain" } 1209 | } 1210 | }, 1211 | { "include": "#type" } 1212 | ] 1213 | }, 1214 | { 1215 | "match": "\\b([A-Z]\\w*)\\b", 1216 | "captures": { 1217 | "1": { "patterns": [{ "include": "#type-variant-name" }] } 1218 | } 1219 | }, 1220 | { 1221 | "comment": "Special support for the List type in Pervasives.", 1222 | "match": "((\\[\\])|(\\[\\.\\.\\.])(\\(a, List\\)))", 1223 | "captures": { 1224 | "2": { "name": "entity.name.function.grain" }, 1225 | "3": { "name": "entity.name.function.grain" }, 1226 | "4": { "patterns": [{ "include": "#type" }] } 1227 | } 1228 | }, 1229 | { 1230 | "comment": "Special support for the constant types in Pervasives.", 1231 | "match": "(true|false|void)", 1232 | "patterns": [{ "include": "#constant" }] 1233 | } 1234 | ] 1235 | }, 1236 | "type-variant-name": { 1237 | "patterns": [ 1238 | { 1239 | "match": "\\b([A-Z]\\w*)\\b", 1240 | "captures": { 1241 | "1": { "name": "entity.name.function.grain" } 1242 | } 1243 | } 1244 | ] 1245 | }, 1246 | "type-variant-constructor": { 1247 | "patterns": [ 1248 | { 1249 | "include": "#comments" 1250 | }, 1251 | { 1252 | "begin": "\\b([A-Z]\\w*)\\b\\s*(\\()", 1253 | "end": "(\\))", 1254 | "beginCaptures": { 1255 | "1": { "patterns": [{ "include": "#type-variant-name" }] }, 1256 | "2": { "name": "punctuation.definition.parameters.begin.grain" } 1257 | }, 1258 | "endCaptures": { 1259 | "1": { "name": "punctuation.definition.parameters.end.grain" } 1260 | }, 1261 | "patterns": [ 1262 | { 1263 | "match": "(,)", 1264 | "captures": { 1265 | "1": { "name": "punctuation.definition.parameters.grain" } 1266 | } 1267 | }, 1268 | { "include": "#expressions" } 1269 | ] 1270 | }, 1271 | { 1272 | "match": "\\b([A-Z]\\w*)\\b", 1273 | "captures": { 1274 | "1": { "patterns": [{ "include": "#type-variant-name" }] } 1275 | } 1276 | }, 1277 | { 1278 | "include": "#constant" 1279 | } 1280 | ] 1281 | }, 1282 | "type-destructor-tuple": { 1283 | "patterns": [ 1284 | { 1285 | "begin": "\\(", 1286 | "end": "\\)", 1287 | "captures": { 1288 | "1": { "name": "punctuation.description.grain" } 1289 | }, 1290 | "patterns": [{ "include": "#type-destructor" }] 1291 | } 1292 | ] 1293 | }, 1294 | "type-destructor-list": { 1295 | "patterns": [ 1296 | { 1297 | "begin": "(\\[>?)", 1298 | "end": "(\\])", 1299 | "captures": { 1300 | "1": { "name": "punctuation.description.grain" } 1301 | }, 1302 | "patterns": [ 1303 | { 1304 | "match": "\\b([a-z]\\w*)\\b", 1305 | "captures": { 1306 | "1": { "name": "variable.language.grain" } 1307 | } 1308 | }, 1309 | { 1310 | "match": ",", 1311 | "name": "punctuation.description.parameters.grain" 1312 | }, 1313 | { 1314 | "match": "\\.\\.\\.", 1315 | "name": "keyword.operator.grain" 1316 | }, 1317 | { "include": "#type-destructor" } 1318 | ] 1319 | } 1320 | ] 1321 | }, 1322 | "type-destructor-record": { 1323 | "patterns": [ 1324 | { 1325 | "begin": "(\\{)", 1326 | "end": "(\\})", 1327 | "captures": { 1328 | "1": { "name": "punctuation.description.grain" } 1329 | }, 1330 | "patterns": [ 1331 | { 1332 | "match": "\\b([a-z]\\w*)\\b\\s*(:)", 1333 | "captures": { 1334 | "1": { "name": "entity.name.grain" }, 1335 | "2": { "name": "punctuation.description.parameters.grain" } 1336 | } 1337 | }, 1338 | { 1339 | "match": "\\b([a-z]\\w*)\\b", 1340 | "captures": { 1341 | "1": { "name": "variable.language.grain" } 1342 | } 1343 | }, 1344 | { 1345 | "match": ",", 1346 | "name": "punctuation.description.parameters.grain" 1347 | }, 1348 | { "include": "#type-destructor" } 1349 | ] 1350 | } 1351 | ] 1352 | }, 1353 | "type-destructor": { 1354 | "patterns": [ 1355 | { 1356 | "include": "#type-annotations" 1357 | }, 1358 | { 1359 | "begin": "\\b([A-Z]\\w*)\\b(?=\\s*\\()", 1360 | "end": "(?<=\\))", 1361 | "beginCaptures": { 1362 | "1": { "patterns": [{ "include": "#type-variant-name" }] } 1363 | }, 1364 | "patterns": [ 1365 | { 1366 | "begin": "(\\()", 1367 | "end": "(\\))", 1368 | "captures": { 1369 | "1": { "name": "punctuation.definition.parameters.grain" } 1370 | }, 1371 | "patterns": [ 1372 | { "include": "#constant" }, 1373 | { 1374 | "match": "(\\||\\bas\\b)", 1375 | "captures": { 1376 | "1": { "name": "keyword.operator.grain" } 1377 | } 1378 | }, 1379 | { 1380 | "match": "\\b([a-z]\\w*)\\b", 1381 | "captures": { 1382 | "1": { "name": "variable.language.grain" } 1383 | } 1384 | }, 1385 | { 1386 | "match": "\\b(_)\\b", 1387 | "captures": { 1388 | "1": { "name": "meta.separator.grain" } 1389 | } 1390 | }, 1391 | { 1392 | "match": "(,)", 1393 | "captures": { 1394 | "1": { "name": "punctuation.definition.parameters.grain" } 1395 | } 1396 | }, 1397 | { 1398 | "include": "#type-destructor" 1399 | } 1400 | ] 1401 | } 1402 | ] 1403 | }, 1404 | { "include": "#constant" }, 1405 | { 1406 | "match": "(\\||\\bas\\b)", 1407 | "captures": { 1408 | "1": { "name": "keyword.operator.grain" } 1409 | } 1410 | }, 1411 | { 1412 | "match": "\\b([a-z]\\w*)\\b", 1413 | "captures": { 1414 | "1": { 1415 | "patterns": [ 1416 | { 1417 | "include": "#identifier" 1418 | } 1419 | ] 1420 | } 1421 | } 1422 | }, 1423 | { 1424 | "match": "\\b([A-Z]\\w*)\\b", 1425 | "captures": { 1426 | "1": { "patterns": [{ "include": "#type-variant-name" }] } 1427 | } 1428 | }, 1429 | { 1430 | "match": "\\b(_)\\b", 1431 | "captures": { 1432 | "1": { "name": "meta.separator.grain" } 1433 | } 1434 | }, 1435 | { "include": "#type-destructor-tuple" }, 1436 | { "include": "#type-destructor-record" }, 1437 | { "include": "#type-destructor-list" } 1438 | ] 1439 | }, 1440 | "pattern-variant": { 1441 | "patterns": [ 1442 | { 1443 | "begin": "(when)", 1444 | "end": "(?==>)", 1445 | "beginCaptures": { 1446 | "1": { "name": "keyword.control.grain" } 1447 | }, 1448 | "patterns": [{ "include": "#expressions" }] 1449 | }, 1450 | { 1451 | "begin": "(=>)\\s*(\\{)", 1452 | "end": "(\\})", 1453 | "beginCaptures": { 1454 | "1": { "name": "keyword.operator.grain" }, 1455 | "2": { "name": "punctuation.definition.parameters.start.grain" } 1456 | }, 1457 | "endCaptures": { 1458 | "1": { "name": "punctuation.definition.parameters.end.grain" } 1459 | }, 1460 | "patterns": [{ "include": "#expressions" }] 1461 | }, 1462 | { 1463 | "begin": "(=>)", 1464 | "end": "((?=,)(? setTimeout(resolve, ms)); 32 | } 33 | 34 | export const getDocPath = (p: string) => { 35 | return path.resolve(__dirname, "./fixture", p); 36 | }; 37 | export const getDocUri = (p: string) => { 38 | return vscode.Uri.file(getDocPath(p)); 39 | }; 40 | 41 | export async function setTestContent(content: string): Promise { 42 | const all = new vscode.Range( 43 | doc.positionAt(0), 44 | doc.positionAt(doc.getText().length) 45 | ); 46 | return editor.edit((eb) => eb.replace(all, content)); 47 | } 48 | -------------------------------------------------------------------------------- /editor-extensions/vscode/test/index.ts: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | * ------------------------------------------------------------------------------------------ */ 5 | import * as path from "path"; 6 | import * as Mocha from "mocha"; 7 | import * as glob from "glob"; 8 | 9 | export function run(): Promise { 10 | // Create the mocha test 11 | const mocha = new Mocha({ 12 | ui: "tdd", 13 | color: true, 14 | }); 15 | mocha.timeout(100000); 16 | 17 | const testsRoot = __dirname; 18 | 19 | return new Promise((resolve, reject) => { 20 | glob("**.test.js", { cwd: testsRoot }, (err, files) => { 21 | if (err) { 22 | return reject(err); 23 | } 24 | 25 | // Add files to the test suite 26 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 27 | 28 | try { 29 | // Run the mocha test 30 | mocha.run((failures) => { 31 | if (failures > 0) { 32 | reject(new Error(`${failures} tests failed.`)); 33 | } else { 34 | resolve(); 35 | } 36 | }); 37 | } catch (err) { 38 | console.error(err); 39 | reject(err); 40 | } 41 | }); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /editor-extensions/vscode/test/runTest.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import * as path from "path"; 6 | 7 | import { runTests } from "@vscode/test-electron"; 8 | 9 | async function main() { 10 | try { 11 | // The folder containing the Extension Manifest package.json 12 | // Passed to `--extensionDevelopmentPath` 13 | const extensionDevelopmentPath = path.resolve(__dirname, "../"); 14 | 15 | // The path to test runner 16 | // Passed to --extensionTestsPath 17 | const extensionTestsPath = path.resolve(__dirname, "./index"); 18 | 19 | // Download VS Code, unzip it and run the integration test 20 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 21 | } catch (err) { 22 | console.error("Failed to run tests"); 23 | process.exit(1); 24 | } 25 | } 26 | 27 | main(); 28 | -------------------------------------------------------------------------------- /editor-extensions/vscode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2022", 5 | "lib": ["ES2022"], 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "sourceMap": true, 9 | "esModuleInterop": true 10 | }, 11 | "include": ["src/*"], 12 | "exclude": ["node_modules"] 13 | } 14 | -------------------------------------------------------------------------------- /grain_shorthand_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grain-lang/grain-language-server/a02dd6336bf237af1ee6fda4d3c606586214e182/grain_shorthand_white.png -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bump-minor-pre-major": true, 3 | "bootstrap-sha": "65112046a7d9742bb2d1f49653bf7a77d62387d8", 4 | "packages": { 5 | "editor-extensions/vscode": { 6 | "release-type": "node" 7 | } 8 | } 9 | } 10 | --------------------------------------------------------------------------------