├── .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 |
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 |
--------------------------------------------------------------------------------