mirror of
https://github.com/fluencelabs/aqua-vscode
synced 2025-04-24 16:12:14 +00:00
Compare commits
87 Commits
1.0.0-main
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
589238f7a5 | ||
|
6314288e73 | ||
|
dbb1f95de0 | ||
|
3c1d2c30e0 | ||
|
876ae6eb8c | ||
|
e597a27197 | ||
|
497a5e17a2 | ||
|
02b01e474a | ||
|
c83cdc9e46 | ||
|
6b628c0df2 | ||
|
ab0ddf6623 | ||
|
d4f883a211 | ||
|
a31e1c78b5 | ||
|
6edf408617 | ||
|
feed343c8a | ||
|
968794ff11 | ||
|
e15a1dbbfb | ||
|
2505865f67 | ||
|
62e063b919 | ||
|
7dd3d60d8e | ||
|
b7c9d02042 | ||
|
9fac848273 | ||
|
d5d0f80bf6 | ||
|
c4411eb54c | ||
|
4b0c776a69 | ||
|
6e05ea7b25 | ||
|
a54e0ed202 | ||
|
8c3f478421 | ||
|
038065fa1f | ||
|
56d4d41e6c | ||
|
0055ff0859 | ||
|
798bc6b08f | ||
|
89fcde5d26 | ||
|
0ff680eb4a | ||
|
1c5d01cd97 | ||
|
309905b680 | ||
|
8abf687b01 | ||
|
6a6c08a569 | ||
|
ba3192f988 | ||
|
3fad69ba77 | ||
|
60bd5335b5 | ||
|
e11ec1bd5b | ||
|
41fd59fe31 | ||
|
fdb17c9266 | ||
|
cbff64a0a2 | ||
|
780246c71e | ||
|
ce32be5c65 | ||
|
3194dfb651 | ||
|
f1ed3059f2 | ||
|
906e40b273 | ||
|
0f56c283a8 | ||
|
d5d3a117b0 | ||
|
6b73358330 | ||
|
4d26bfe686 | ||
|
8ea3fdcb19 | ||
|
d5d96c1f68 | ||
|
9e3ef257a7 | ||
|
16701cf166 | ||
|
7998503155 | ||
|
38e5e14774 | ||
|
e1970cdbe3 | ||
|
802e4c3810 | ||
|
7c7e80bd75 | ||
|
ae72f62dbe | ||
|
7a71f0fbbe | ||
|
03169bef44 | ||
|
9136b322fc | ||
|
95fe2adfc9 | ||
|
8be467370b | ||
|
4d35b17f9a | ||
|
620534b6ec | ||
|
f2866f8075 | ||
|
f387caa792 | ||
|
b5f81af561 | ||
|
f90d98d9e5 | ||
|
c3e78a2a19 | ||
|
29c1d264ea | ||
|
775c025174 | ||
|
bdb97b1788 | ||
|
e756255578 | ||
|
a3ae73dd86 | ||
|
d95e145869 | ||
|
801ffbd26e | ||
|
c97c220177 | ||
|
03148eae86 | ||
|
73c68481c3 | ||
|
433ce73acc |
14
.eslintrc.json
Normal file
14
.eslintrc.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {}
|
||||
}
|
8
.github/renovate.json
vendored
Normal file
8
.github/renovate.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": [
|
||||
"github>fluencelabs/renovate",
|
||||
"github>fluencelabs/renovate:npm"
|
||||
],
|
||||
"ignorePaths": ["api/aqua-api-example/**"],
|
||||
"enabledManagers": ["npm"]
|
||||
}
|
64
.github/workflows/publish.yml
vendored
Normal file
64
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
name: 'Build and release Aqua VSCE'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: 'Publish'
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install jq
|
||||
run: sudo apt-get update && sudo apt-get --yes --force-yes install jq
|
||||
|
||||
- name: Get version from npm and increment
|
||||
run: |
|
||||
VERSION="1.0.${{ github.run_number }}"
|
||||
PKG_NAME="$(cat package.json | jq -r .name)"
|
||||
|
||||
# save info to env
|
||||
echo "FINAL_VERSION=$VERSION" | tee -a $GITHUB_ENV
|
||||
echo "PKG_NAME=$PKG_NAME" | tee -a $GITHUB_ENV
|
||||
echo "PKG_FILE=${PKG_NAME}-${VERSION}.vsix" | tee -a $GITHUB_ENV
|
||||
|
||||
- name: Set version to ${{ env.FINAL_VERSION }}
|
||||
run: |
|
||||
yarn version --new-version ${{ env.FINAL_VERSION }} --no-git-tag-version
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Package and publish
|
||||
run: |
|
||||
npm i
|
||||
npm i -g vsce
|
||||
vsce package
|
||||
vsce publish -p ${{ secrets.VSCE_PAT }}
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ env.FINAL_VERSION }}
|
||||
name: Aqua VSCE ${{ env.FINAL_VERSION }}
|
||||
body: |
|
||||
Version: ${{ env.FINAL_VERSION }}
|
||||
files: |
|
||||
${{ env.PKG_FILE }}
|
||||
draft: true
|
||||
prerelease: false
|
34
.github/workflows/run-tests.yml
vendored
Normal file
34
.github/workflows/run-tests.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
name: 'test'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/tests.yml'
|
||||
- '!.github/workflows/run-tests.yml'
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/tests.yml'
|
||||
- '!.github/workflows/run-tests.yml'
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: 'Integration tests'
|
||||
uses: ./.github/workflows/tests.yml
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
unit-tests:
|
||||
name: 'Unit tests'
|
||||
uses: ./.github/workflows/unit-tests.yml
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
53
.github/workflows/tests.yml
vendored
Normal file
53
.github/workflows/tests.yml
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
name: Run tests with workflow_call
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'git ref to checkout to'
|
||||
type: string
|
||||
default: 'main'
|
||||
fcli-version:
|
||||
description: 'fcli version to use'
|
||||
type: string
|
||||
default: 'main'
|
||||
|
||||
env:
|
||||
FORCE_COLOR: true
|
||||
|
||||
jobs:
|
||||
aqua-vscode:
|
||||
name: 'Run integration tests'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: fluencelabs/aqua-vscode
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
|
||||
- name: Setup fcli
|
||||
uses: fluencelabs/setup-fluence@v1
|
||||
with:
|
||||
artifact: fcli
|
||||
version: ${{ inputs.fcli-version }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run before-tests
|
||||
run: npm run before-tests
|
||||
|
||||
- name: Run tests
|
||||
uses: coactions/setup-xvfb@v1
|
||||
with:
|
||||
run: npm run test
|
39
.github/workflows/unit-tests.yml
vendored
Normal file
39
.github/workflows/unit-tests.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: Run tests with workflow_call
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'git ref to checkout to'
|
||||
type: string
|
||||
default: 'main'
|
||||
|
||||
env:
|
||||
FORCE_COLOR: true
|
||||
|
||||
jobs:
|
||||
aqua-vscode:
|
||||
name: 'Run unit tests'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: fluencelabs/aqua-vscode
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
uses: coactions/setup-xvfb@v1
|
||||
with:
|
||||
run: npm run test:unit
|
97
.github/workflows/vscode_highlight.yml
vendored
97
.github/workflows/vscode_highlight.yml
vendored
@ -1,97 +0,0 @@
|
||||
name: "build vscode syntax highlight"
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
npm-publish:
|
||||
name: "Publish"
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
### Extract branch name
|
||||
- name: Extract branch name
|
||||
if: github.event_name != 'pull_request'
|
||||
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
|
||||
id: extract_branch
|
||||
|
||||
- name: Extract branch name
|
||||
if: github.event_name == 'pull_request'
|
||||
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
### Calculate FINAL_VERSION
|
||||
- name: Install jq
|
||||
run: sudo apt-get update && sudo apt-get --yes --force-yes install jq
|
||||
|
||||
- name: Get version from npm and increment
|
||||
run: |
|
||||
cd vscode-highlight
|
||||
# install semver and add it to PATH
|
||||
yarn global add semver
|
||||
PATH="$(yarn global bin):$PATH"
|
||||
|
||||
# sanitize branch name so it can be used as a semver suffix (replace [^0-9a-zA-Z-] with hyphen)
|
||||
SANITIZED_BRANCH="$(echo -n "${{ env.BRANCH_NAME }}" | tr -C '[:alnum:]-' -)"
|
||||
# get package name from package.json
|
||||
PKG_NAME="$(cat package.json | jq -r .name)"
|
||||
|
||||
# take all versions from npm and replace single quotes with double quotes
|
||||
NPM_VERSIONS=$(yarn info --silent "$PKG_NAME" versions 2>/dev/null | tr \' \")
|
||||
# take only versions that contain branch name
|
||||
NPM_VERSIONS_FILTERED=$(echo $NPM_VERSIONS | jq -r ".[] | select(contains(\"$SANITIZED_BRANCH\"))")
|
||||
# flatten into a single line
|
||||
NPM_VERSIONS_FLATTENED=$(echo $NPM_VERSIONS_FILTERED | awk '{print}' ORS=' ')
|
||||
# sort versions according to semver, take highest (last)
|
||||
LAST_NPM_VERSION="$(semver -p $(echo $NPM_VERSIONS_FLATTENED) | tail -n1 || true)"
|
||||
# increment prerelease part of the version
|
||||
PRERELEASE_NPM_VERSION="$(semver --increment prerelease --preid "$SANITIZED_BRANCH" "${LAST_NPM_VERSION}" || true)"
|
||||
|
||||
# take local version
|
||||
LOCAL_VERSION="$(cat package.json | jq -r .version)"
|
||||
# set prerelease part on local version
|
||||
LOCAL_PRERELEASE_VERSION="$(semver --increment prerelease --preid "$SANITIZED_BRANCH" "${LOCAL_VERSION}-0")" # added '-0' here to avoid semver erroneously increment patch octet. Any suffix works, '-0' is chosen deliberately.
|
||||
|
||||
# take the highest version
|
||||
MAX_VERSION="$(semver "$LOCAL_PRERELEASE_VERSION" "$PRERELEASE_NPM_VERSION" | tail -n1)"
|
||||
|
||||
# save info to env
|
||||
echo "FINAL_VERSION=$MAX_VERSION" | tee -a $GITHUB_ENV
|
||||
echo "PKG_NAME=$PKG_NAME" | tee -a $GITHUB_ENV
|
||||
echo "PKG_FILE=vscode-highlight/${PKG_NAME}-${FINAL_VERSION}.tgz" | tee -a $GITHUB_ENV
|
||||
|
||||
### Set version
|
||||
- name: Set version to ${{ env.FINAL_VERSION }}
|
||||
run: |
|
||||
cd vscode-highlight
|
||||
yarn version --new-version ${{ env.FINAL_VERSION }} --no-git-tag-version
|
||||
|
||||
### Pack npm package
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: "15"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- run: |
|
||||
cd vscode-highlight
|
||||
npm pack
|
||||
|
||||
### Create a pre-release
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ env.FINAL_VERSION }}
|
||||
release_name: VSCode syntax highlighting plugin ${{ env.FINAL_VERSION }}
|
||||
files: |
|
||||
${{ env.PKG_FILE }}
|
||||
body: |
|
||||
Version: ${{ env.FINAL_VERSION }}
|
||||
draft: false
|
||||
prerelease: false
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
node_modules
|
||||
*.vsix
|
||||
*.tgz
|
||||
.vscode-test
|
||||
|
||||
server/out
|
||||
client/out
|
||||
integration-tests/out
|
||||
integration-tests/test-workspace/fluenceProject
|
||||
!integration-tests/test-workspace/fluenceProject/src/aqua/test.aqua
|
8
.prettierrc.js
Normal file
8
.prettierrc.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
semi: true,
|
||||
trailingComma: 'all',
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
tabWidth: 4,
|
||||
useTabs: false,
|
||||
};
|
6
.vscode-test.js
Normal file
6
.vscode-test.js
Normal file
@ -0,0 +1,6 @@
|
||||
const { defineConfig } = require('@vscode/test-cli');
|
||||
|
||||
module.exports = defineConfig({
|
||||
workspaceFolder: './integration-tests/test-workspace/',
|
||||
files: 'integration-tests/out/**/*.test.js',
|
||||
});
|
@ -3,15 +3,13 @@
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
]
|
||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
.gitignore
|
||||
vsc-extension-quickstart.md
|
||||
.github
|
||||
integration-tests
|
||||
**/src/test
|
@ -6,4 +6,4 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
||||
- Initial release
|
13
CONTRIBUTING.md
Normal file
13
CONTRIBUTING.md
Normal file
@ -0,0 +1,13 @@
|
||||
## Contribute Code
|
||||
|
||||
You are welcome to contribute to Fluence!
|
||||
|
||||
Things you need to know:
|
||||
|
||||
1. You need to **agree to the [Contributor License Agreement](https://gist.github.com/fluencelabs-org/3f4cbb3cc14c1c0fb9ad99d8f7316ed7) (CLA)**. This is a common practice in all major Open Source projects. At the current moment, we are unable to accept contributions made on behalf of a company. Only individual contributions will be accepted.
|
||||
|
||||
2. **Not all proposed contributions can be accepted**. Some features may, e.g., just fit a third-party add-on better. The contribution must fit the overall direction of Fluence and really improve it. The more effort you invest, the better you should clarify in advance whether the contribution fits: the best way would be to just open an issue to discuss the contribution you plan to make.
|
||||
|
||||
### Contributor License Agreement
|
||||
|
||||
When you contribute, you have to be aware that your contribution is covered by **[Apache License 2.0](./LICENSE)**, but might relicensed under few other software licenses mentioned in the **Contributor License Agreement**. In particular, you need to agree to the Contributor License Agreement. If you agree to its content, you simply have to click on the link posted by the CLA assistant as a comment to the pull request. Click it to check the CLA, then accept it on the following screen if you agree to it. The CLA assistant will save this decision for upcoming contributions and will notify you if there is any change to the CLA in the meantime.
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
54
README.md
54
README.md
@ -1,3 +1,53 @@
|
||||
# Collection of tools for aqua language
|
||||
# Aqua Syntax Highlighting
|
||||
|
||||
The tool enables syntax highlighting for [Aqua](https://github.com/fluencelabs/aqua) programming language, compilation on file changes, and go-to definition in Visual Studio.
|
||||
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
Installation is pretty simple:
|
||||
|
||||
1. Install [the extension](https://marketplace.visualstudio.com/items?itemName=FluenceLabs.aqua).
|
||||
|
||||
2. Configure colors for Aqua-specific tokens if needed (see below).
|
||||
|
||||
Add the following lines to the [`settings.json`](https://code.visualstudio.com/docs/getstarted/settings) file. Feel free to choose colors according to your favorite theme. In the example below, services will be highlighted as green and all keywords that affect the topology will be highlighted as red.
|
||||
|
||||
```json
|
||||
"editor.tokenColorCustomizations": {
|
||||
"textMateRules": [
|
||||
{
|
||||
"scope": "keyword.topology.aqua",
|
||||
"settings": {
|
||||
"foreground": "#FF0000",
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "support.service.aqua",
|
||||
"settings": {
|
||||
"foreground": "#00FF00",
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
More details can be found [here](vsc-extension-quickstart.md).
|
||||
|
||||
NOTE: if you're going to change pattern names, check out the naming rules in [TextMate Grammar doc](https://macromates.com/manual/en/language_grammars). You have to use the predefined pattern naming scheme, or the syntax won't be highlighted.
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
Please, file an [issue](https://github.com/fluencelabs/aqua-vscode/issues) if you find a bug. You can also contact us at [Discord](https://discord.com/invite/5qSnPZKh7u) or [Telegram](https://t.me/fluence_project). We will do our best to resolve the issue ASAP.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Any interested person is welcome to contribute to the project. Please, make sure you read and follow some basic [rules](./CONTRIBUTING.md).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
All software code is copyright (c) Fluence Labs, Inc. under the [Apache-2.0](./LICENSE) license.
|
||||
|
||||
TBD
|
||||
|
195
client/package-lock.json
generated
Normal file
195
client/package-lock.json
generated
Normal file
@ -0,0 +1,195 @@
|
||||
{
|
||||
"name": "aqua-ls-client",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "aqua-ls-client",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "1.94.0"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "1.94.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/vscode": {
|
||||
"version": "1.94.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.94.0.tgz",
|
||||
"integrity": "sha512-UyQOIUT0pb14XSqJskYnRwD2aG0QrPVefIfrW1djR+/J4KeFQ0i1+hjZoaAmeNf3Z2jleK+R2hv+EboG/m8ruw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-jsonrpc": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
||||
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageclient": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
|
||||
"integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
|
||||
"dependencies": {
|
||||
"minimatch": "^5.1.0",
|
||||
"semver": "^7.3.7",
|
||||
"vscode-languageserver-protocol": "3.17.5"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.82.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-protocol": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
|
||||
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
|
||||
"dependencies": {
|
||||
"vscode-jsonrpc": "8.2.0",
|
||||
"vscode-languageserver-types": "3.17.5"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-types": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
||||
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/vscode": {
|
||||
"version": "1.94.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.94.0.tgz",
|
||||
"integrity": "sha512-UyQOIUT0pb14XSqJskYnRwD2aG0QrPVefIfrW1djR+/J4KeFQ0i1+hjZoaAmeNf3Z2jleK+R2hv+EboG/m8ruw==",
|
||||
"dev": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||
"requires": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"vscode-jsonrpc": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
||||
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="
|
||||
},
|
||||
"vscode-languageclient": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
|
||||
"integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
|
||||
"requires": {
|
||||
"minimatch": "^5.1.0",
|
||||
"semver": "^7.3.7",
|
||||
"vscode-languageserver-protocol": "3.17.5"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-protocol": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
|
||||
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
|
||||
"requires": {
|
||||
"vscode-jsonrpc": "8.2.0",
|
||||
"vscode-languageserver-types": "3.17.5"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-types": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
||||
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
}
|
20
client/package.json
Normal file
20
client/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "aqua-ls-client",
|
||||
"description": "VSCode part of an aqua language server",
|
||||
"author": "Fluence Labs",
|
||||
"version": "0.0.1",
|
||||
"publisher": "vscode",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fluencelabs/aqua"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "1.94.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "1.94.0"
|
||||
}
|
||||
}
|
47
client/src/extension.ts
Normal file
47
client/src/extension.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import * as path from 'path';
|
||||
import type { ExtensionContext } from 'vscode';
|
||||
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node';
|
||||
|
||||
let client: LanguageClient;
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
// The server is implemented in node
|
||||
const serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js'));
|
||||
// The debug options for the server
|
||||
// --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
|
||||
const debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] };
|
||||
|
||||
// If the extension is launched in debug mode then the debug server options are used
|
||||
// Otherwise the run options are used
|
||||
const serverOptions: ServerOptions = {
|
||||
run: {
|
||||
module: serverModule,
|
||||
transport: TransportKind.ipc,
|
||||
},
|
||||
debug: {
|
||||
module: serverModule,
|
||||
transport: TransportKind.ipc,
|
||||
options: debugOptions,
|
||||
},
|
||||
};
|
||||
|
||||
// Options to control the language client
|
||||
const clientOptions: LanguageClientOptions = {
|
||||
// Register the server for aqua source files
|
||||
documentSelector: [{ pattern: '**/*.aqua' }],
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
client = new LanguageClient('aqua-language-server', 'Aqua Language Server', serverOptions, clientOptions);
|
||||
|
||||
// Start the client. This will also launch the server
|
||||
client.start();
|
||||
}
|
||||
|
||||
export function deactivate(): Thenable<void> | undefined {
|
||||
if (!client) {
|
||||
return undefined;
|
||||
}
|
||||
return client.stop();
|
||||
}
|
12
client/tsconfig.json
Normal file
12
client/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2020",
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"],
|
||||
"extends": "@tsconfig/node16-strictest/tsconfig.json"
|
||||
}
|
14
integration-tests/before-tests.sh
Normal file
14
integration-tests/before-tests.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
cd "$SCRIPT_DIR/test-workspace"
|
||||
|
||||
# Install deps for npm package
|
||||
npm i -C npmPackage/
|
||||
|
||||
# Prepare fluence project
|
||||
fluence init -t minimal fluenceProject --no-input
|
||||
cd fluenceProject
|
||||
fluence dep i
|
108
integration-tests/src/extension.test.ts
Normal file
108
integration-tests/src/extension.test.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
function delay(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function waitFor(cond: () => boolean, timeout: number): Promise<boolean> {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (Date.now() - startTime < timeout) {
|
||||
if (cond()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
await delay(100);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
async function waitForExtensionActivation(timeout: number): Promise<void> {
|
||||
const activated = waitFor(() => {
|
||||
return (
|
||||
vscode.extensions.all.find((extension) => {
|
||||
return extension.isActive && extension.id === 'FluenceLabs.aqua';
|
||||
}) !== undefined
|
||||
);
|
||||
}, timeout);
|
||||
|
||||
assert.ok(activated, `Aqua extension was not activated after ${timeout}ms`);
|
||||
}
|
||||
|
||||
async function openDocument(relPath: string): Promise<vscode.TextDocument> {
|
||||
assert.ok(vscode.workspace.workspaceFolders?.length === 1, 'Test workspace should contain one folder');
|
||||
|
||||
const rootPath = vscode.workspace.workspaceFolders[0]!.uri.fsPath;
|
||||
const filePath = path.join(rootPath, relPath);
|
||||
const fileUri = vscode.Uri.file(filePath);
|
||||
|
||||
const document = await vscode.workspace.openTextDocument(fileUri);
|
||||
|
||||
await waitForExtensionActivation(10000);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
async function getDiagnostics(uri: vscode.Uri, timeout: number): Promise<vscode.Diagnostic[]> {
|
||||
let diagnostics: vscode.Diagnostic[] = [];
|
||||
|
||||
const got = await waitFor(() => {
|
||||
diagnostics = vscode.languages.getDiagnostics(uri);
|
||||
return diagnostics.length > 0;
|
||||
}, timeout);
|
||||
|
||||
assert.ok(got, `No diagnostics provided for ${uri.toString()} in ${timeout}ms`);
|
||||
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
test('Semantic errors in single file', async () => {
|
||||
const document = await openDocument('singleFile/file.aqua');
|
||||
const diagnostics = await getDiagnostics(document.uri, 10000);
|
||||
|
||||
assert.ok(
|
||||
diagnostics.find((diagnostic) => {
|
||||
return (
|
||||
diagnostic.severity === vscode.DiagnosticSeverity.Error &&
|
||||
diagnostic.message.includes('Wrong value type')
|
||||
);
|
||||
}),
|
||||
'No error diagnostics provided',
|
||||
);
|
||||
}).timeout(10000);
|
||||
|
||||
test('Semantic errors in npm package', async () => {
|
||||
const document = await openDocument('npmPackage/main.aqua');
|
||||
const diagnostics = await getDiagnostics(document.uri, 10000);
|
||||
|
||||
assert.ok(
|
||||
diagnostics.find((diagnostic) => {
|
||||
return (
|
||||
diagnostic.severity === vscode.DiagnosticSeverity.Error &&
|
||||
diagnostic.message.includes('Wrong value type')
|
||||
);
|
||||
}),
|
||||
'No error diagnostics provided',
|
||||
);
|
||||
}).timeout(10000);
|
||||
|
||||
test('Semantic errors in fluence project', async () => {
|
||||
const document = await openDocument('fluenceProject/src/aqua/test.aqua');
|
||||
const diagnostics = await getDiagnostics(document.uri, 10000);
|
||||
|
||||
assert.ok(
|
||||
diagnostics.find((diagnostic) => {
|
||||
return (
|
||||
diagnostic.severity === vscode.DiagnosticSeverity.Error &&
|
||||
diagnostic.message.includes('Wrong value type')
|
||||
);
|
||||
}),
|
||||
'No error diagnostics provided',
|
||||
);
|
||||
}).timeout(10000);
|
||||
});
|
@ -0,0 +1,7 @@
|
||||
aqua Test declares *
|
||||
|
||||
use "@fluencelabs/aqua-lib/builtin.aqua" as Builtin
|
||||
|
||||
func test() -> i64:
|
||||
res <- Builtin.Op.concat_strings("a", "b")
|
||||
<- res
|
7
integration-tests/test-workspace/npmPackage/main.aqua
Normal file
7
integration-tests/test-workspace/npmPackage/main.aqua
Normal file
@ -0,0 +1,7 @@
|
||||
aqua Main declares *
|
||||
|
||||
use "@fluencelabs/aqua-lib/builtin.aqua" as Builtin
|
||||
|
||||
func main() -> i64:
|
||||
res <- Builtin.Op.concat_strings("a", "b")
|
||||
<- res
|
22
integration-tests/test-workspace/npmPackage/package-lock.json
generated
Normal file
22
integration-tests/test-workspace/npmPackage/package-lock.json
generated
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "Test Aqua NPM Package",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "Test Aqua NPM Package",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fluencelabs/aqua-lib": "0.12.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@fluencelabs/aqua-lib": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@fluencelabs/aqua-lib/-/aqua-lib-0.12.1.tgz",
|
||||
"integrity": "sha512-iqCoBlsc7Zr9eqFg2CBKsUUgu44viBLnUkP0fhz8vsYYzNi54/zBEpi8Vtr7JPZwPsH/ee09FSstPf6aGKiERw==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
12
integration-tests/test-workspace/npmPackage/package.json
Normal file
12
integration-tests/test-workspace/npmPackage/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "Test Aqua NPM Package",
|
||||
"version": "1.0.0",
|
||||
"files": [
|
||||
"main.aqua"
|
||||
],
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fluencelabs/aqua-lib": "0.12.1"
|
||||
}
|
||||
}
|
4
integration-tests/test-workspace/singleFile/file.aqua
Normal file
4
integration-tests/test-workspace/singleFile/file.aqua
Normal file
@ -0,0 +1,4 @@
|
||||
aqua Test
|
||||
|
||||
func test() -> string:
|
||||
<- 42
|
12
integration-tests/tsconfig.json
Normal file
12
integration-tests/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "ES5",
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"],
|
||||
"extends": "@tsconfig/node16-strictest/tsconfig.json"
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "--",
|
||||
"lineComment": ";"
|
||||
},
|
||||
"brackets": [
|
||||
["[", "]"],
|
||||
@ -10,10 +10,12 @@
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["%", "%"]
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["%", "%"]
|
||||
]
|
||||
}
|
||||
}
|
23
language-configurations/aqua.json
Normal file
23
language-configurations/aqua.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "--"
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["%", "%"]
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["%", "%"]
|
||||
]
|
||||
}
|
5040
package-lock.json
generated
Normal file
5040
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
157
package.json
Normal file
157
package.json
Normal file
@ -0,0 +1,157 @@
|
||||
{
|
||||
"name": "aqua",
|
||||
"displayName": "Aqua",
|
||||
"description": "Aqua language support powered by the Aqua Language Server",
|
||||
"author": "Fluence Labs",
|
||||
"version": "1.1.0",
|
||||
"publisher": "FluenceLabs",
|
||||
"icon": "icon.png",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fluencelabs/aqua"
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages"
|
||||
],
|
||||
"keywords": [
|
||||
"aqua vscode extension"
|
||||
],
|
||||
"engines": {
|
||||
"vscode": "1.94.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onLanguage:aqua"
|
||||
],
|
||||
"main": "./client/out/extension",
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "aqua",
|
||||
"aliases": [
|
||||
"aqua"
|
||||
],
|
||||
"extensions": [
|
||||
".aqua"
|
||||
],
|
||||
"configuration": "./language-configurations/aqua.json"
|
||||
},
|
||||
{
|
||||
"id": "air",
|
||||
"aliases": [
|
||||
"AIR"
|
||||
],
|
||||
"extensions": [
|
||||
".air"
|
||||
],
|
||||
"configuration": "./language-configurations/air.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "aqua",
|
||||
"scopeName": "source.aqua",
|
||||
"path": "./syntaxes/aqua.tmLanguage.json"
|
||||
},
|
||||
{
|
||||
"language": "air",
|
||||
"scopeName": "source.air",
|
||||
"path": "./syntaxes/air.tmLanguage.json"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "Aqua",
|
||||
"properties": {
|
||||
"aquaSettings.imports": {
|
||||
"definitions": {
|
||||
"legacyImports": {
|
||||
"description": "Legacy format of imports - just an array of paths",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"structuredImports": {
|
||||
"description": "Structured format of imports - dict of settings for path prefixes",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"description": "Settings for path prefix - dict of locations for import prefixes",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"description": "Location for import prefix",
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scope": "resource",
|
||||
"type": [
|
||||
"object",
|
||||
"array"
|
||||
],
|
||||
"if": {
|
||||
"type": "object"
|
||||
},
|
||||
"then": {
|
||||
"$ref": "#/definitions/structuredImports"
|
||||
},
|
||||
"else": {
|
||||
"$ref": "#/definitions/legacyImports"
|
||||
},
|
||||
"default": {},
|
||||
"description": "Adds imports for aqua file or project"
|
||||
},
|
||||
"aquaSettings.fluencePath": {
|
||||
"scope": "resource",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null,
|
||||
"description": "Path to fluence CLI executable"
|
||||
},
|
||||
"aquaSettings.fluenceCallDelay": {
|
||||
"scope": "resource",
|
||||
"type": "number",
|
||||
"default": 5000,
|
||||
"description": "Minimal delay (ms) between calls to fluence CLI (for one file)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"before-tests": "bash integration-tests/before-tests.sh",
|
||||
"test": "npm run compile && vscode-test",
|
||||
"test:unit": "npm run test -C server",
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -b",
|
||||
"watch": "tsc -b -w",
|
||||
"lint": "eslint ./client/src ./server/src --ext .ts,.tsx",
|
||||
"postinstall": "cd client && npm install && cd ../server && npm install && cd .."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16-strictest": "1.0.4",
|
||||
"@types/mocha": "9.1.1",
|
||||
"@types/node": "14.18.63",
|
||||
"@typescript-eslint/eslint-plugin": "7.9.0",
|
||||
"@typescript-eslint/parser": "7.9.0",
|
||||
"@vscode/test-cli": "0.0.9",
|
||||
"@vscode/test-electron": "2.4.1",
|
||||
"@types/vscode": "1.94.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-standard": "17.1.0",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-n": "15.7.0",
|
||||
"eslint-plugin-promise": "6.1.1",
|
||||
"mocha": "9.2.2",
|
||||
"prettier": "2.6.2",
|
||||
"typescript": "4.9.5"
|
||||
}
|
||||
}
|
8
renovate.json
Normal file
8
renovate.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": [
|
||||
"github>fluencelabs/renovate",
|
||||
"github>fluencelabs/renovate:npm"
|
||||
],
|
||||
"ignorePaths": ["api/aqua-api-example/**"],
|
||||
"enabledManagers": ["npm"]
|
||||
}
|
3
server/.mocharc.cjs
Normal file
3
server/.mocharc.cjs
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
spec: 'src/test/**/*.test.ts',
|
||||
};
|
2221
server/package-lock.json
generated
Normal file
2221
server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
server/package.json
Normal file
28
server/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "aqua-ls-server",
|
||||
"description": "Implementation of an aqua language server in node.",
|
||||
"version": "0.0.1",
|
||||
"author": "Fluence Labs",
|
||||
"engines": {
|
||||
"node": "22"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fluencelabs/aqua"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluencelabs/aqua-language-server-api": "0.14.11",
|
||||
"global-dirs": "4.0.0",
|
||||
"vscode-languageserver": "9.0.1",
|
||||
"vscode-languageserver-textdocument": "1.0.12",
|
||||
"vscode-uri": "3.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "10.0.6",
|
||||
"ts-mocha": "10.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "ts-mocha",
|
||||
"get-root": "npm root --global"
|
||||
}
|
||||
}
|
56
server/src/cli.ts
Normal file
56
server/src/cli.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { exec } from 'child_process';
|
||||
import { dirname } from 'path';
|
||||
|
||||
import type { Imports } from './imports';
|
||||
import { normalizeImports } from './imports';
|
||||
|
||||
export class FluenceCli {
|
||||
readonly cliPath: string;
|
||||
|
||||
/**
|
||||
* @param cliPath Path to `fluence` executable.
|
||||
* Defaults to `fluence` (i.e. it should be in PATH).
|
||||
*/
|
||||
constructor(cliPath?: string) {
|
||||
this.cliPath = cliPath || 'fluence';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns output of `fluence aqua imports`
|
||||
* in dir of @param filePath.
|
||||
*/
|
||||
async imports(filePath: string): Promise<Imports> {
|
||||
const cwd = dirname(filePath);
|
||||
const result = await this.runJson(['aqua', 'imports'], cwd);
|
||||
try {
|
||||
return normalizeImports(result);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error(`Error converting imports from fluence: ${e.message}`);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs `fluence` with given arguments and returns its stdout as JSON.
|
||||
*/
|
||||
private async runJson(args: string[], cwd?: string | undefined): Promise<JSON> {
|
||||
const cmd = `${this.cliPath} ${args.join(' ')}`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(cmd, { cwd }, (err, stdout, _) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else
|
||||
try {
|
||||
const result = JSON.parse(stdout);
|
||||
resolve(result);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
68
server/src/imports.ts
Normal file
68
server/src/imports.ts
Normal file
@ -0,0 +1,68 @@
|
||||
export type Imports = Record<string, Record<string, string[]>>;
|
||||
|
||||
// Normalize imports to the new format
|
||||
// Legacy format of an array of paths is converted to the new format
|
||||
// Empty imports are converted to an empty object
|
||||
export function normalizeImports(imports: unknown): Imports {
|
||||
// Empty imports
|
||||
if (imports === undefined || imports === null) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Legacy imports - array of paths
|
||||
if (Array.isArray(imports) && imports.every((i) => typeof i === 'string')) {
|
||||
return {
|
||||
'/': {
|
||||
'': imports as string[],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const isStringArray = (v: unknown): v is string[] => {
|
||||
return Array.isArray(v) && v.every((i) => typeof i === 'string');
|
||||
};
|
||||
|
||||
// New imports - object of objects of paths or arrays of paths
|
||||
// Inner single paths are normalized to arrays
|
||||
if (typeof imports === 'object') {
|
||||
for (const info of Object.values(imports)) {
|
||||
if (typeof info !== 'object') {
|
||||
throw new Error(`Invalid imports: ${JSON.stringify(imports)}`);
|
||||
}
|
||||
|
||||
for (const [importPrefix, locations] of Object.entries(info)) {
|
||||
if (typeof locations === 'string') {
|
||||
info[importPrefix] = [locations];
|
||||
} else if (!isStringArray(locations)) {
|
||||
throw new Error(`Invalid imports: ${JSON.stringify(imports)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imports as Imports;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid imports: ${JSON.stringify(imports)}`);
|
||||
}
|
||||
|
||||
// Deep merge two import settings, overriding the first with the second
|
||||
export function uniteImports(pre: Imports, post: Imports): Imports {
|
||||
const result: Imports = { ...pre };
|
||||
for (const [pathPrefix, info] of Object.entries(post)) {
|
||||
const resultInfo = result[pathPrefix];
|
||||
if (resultInfo) {
|
||||
for (const [importPrefix, paths] of Object.entries(info)) {
|
||||
const importInfo = resultInfo[importPrefix];
|
||||
if (importInfo) {
|
||||
resultInfo[importPrefix] = [...importInfo, ...paths];
|
||||
} else {
|
||||
resultInfo[importPrefix] = paths;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result[pathPrefix] = info;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
74
server/src/info.ts
Normal file
74
server/src/info.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import type { DocumentUri, Position } from 'vscode-languageserver';
|
||||
|
||||
import type { TokenInfo, TokenLink, TokenLocation } from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
|
||||
|
||||
import { locInLoc, posInToken } from './utils';
|
||||
|
||||
/**
|
||||
* Class that incapsulates information
|
||||
* about tokens inside a document
|
||||
*/
|
||||
export class DocumentInfo {
|
||||
private links: TokenLink[];
|
||||
private tokens: TokenInfo[];
|
||||
private path: string;
|
||||
|
||||
constructor(path: string, links: TokenLink[], tokens: TokenInfo[]) {
|
||||
this.path = path;
|
||||
this.links = links;
|
||||
this.tokens = tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find token info at position
|
||||
* @param pos position to check
|
||||
* @returns token info if found
|
||||
*/
|
||||
infoAt(pos: Position): TokenInfo | undefined {
|
||||
const info = this.tokens.find((token) => posInToken(this.path, pos, token.location));
|
||||
if (info) {
|
||||
return info;
|
||||
}
|
||||
|
||||
// Fallback: if token info is not found
|
||||
// try to find its definition and its info
|
||||
const def = this.defAt(pos);
|
||||
if (def) {
|
||||
return this.tokens.find((token) => locInLoc(def, token.location));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find definition location at position
|
||||
* @param pos Position to check
|
||||
* @returns Definition location if found
|
||||
*/
|
||||
defAt(pos: Position): TokenLocation | undefined {
|
||||
return this.links.find((link) => posInToken(this.path, pos, link.current))?.definition;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Info manager for documents
|
||||
*/
|
||||
export class InfoManager {
|
||||
private documents: Map<DocumentUri, DocumentInfo> = new Map();
|
||||
|
||||
updateDocumentInfo(uri: DocumentUri, info: DocumentInfo) {
|
||||
this.documents.set(uri, info);
|
||||
}
|
||||
|
||||
infoAt(uri: DocumentUri, pos: Position): TokenInfo | undefined {
|
||||
return this.documents.get(uri)?.infoAt(pos);
|
||||
}
|
||||
|
||||
defAt(uri: DocumentUri, pos: Position): TokenLocation | undefined {
|
||||
return this.documents.get(uri)?.defAt(pos);
|
||||
}
|
||||
|
||||
removeDocumentInfo(uri: DocumentUri) {
|
||||
this.documents.delete(uri);
|
||||
}
|
||||
}
|
165
server/src/server.ts
Normal file
165
server/src/server.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import {
|
||||
createConnection,
|
||||
DiagnosticSeverity,
|
||||
DidChangeConfigurationNotification,
|
||||
InitializeParams,
|
||||
InitializeResult,
|
||||
ProposedFeatures,
|
||||
TextDocuments,
|
||||
TextDocumentSyncKind,
|
||||
} from 'vscode-languageserver/node';
|
||||
import type { WorkspaceFolder } from 'vscode-languageserver-protocol';
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||
import type { DefinitionParams, Hover, HoverParams, MarkupContent } from 'vscode-languageserver';
|
||||
import { MarkupKind } from 'vscode-languageserver';
|
||||
|
||||
import { compileAqua } from './validation';
|
||||
import { FluenceCli } from './cli';
|
||||
import { Settings, SettingsManager } from './settings';
|
||||
import { InfoManager } from './info';
|
||||
import { tokenToLocation } from './utils';
|
||||
import { typeToString } from './types';
|
||||
|
||||
// Create a connection to the server, using Node's IPC as a transport.
|
||||
// Also include all preview / proposed LSP features.
|
||||
const connection = createConnection(ProposedFeatures.all);
|
||||
|
||||
// Create a simple text document manager.
|
||||
const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
||||
|
||||
let hasConfigurationCapability = false;
|
||||
let hasWorkspaceFolderCapability = false;
|
||||
let folders: WorkspaceFolder[] = [];
|
||||
|
||||
function createSettingsManager(cliPath?: string, cliCallDelay?: number, defaultSettings?: Settings): SettingsManager {
|
||||
const cli = new FluenceCli(cliPath);
|
||||
const configuration = hasConfigurationCapability ? connection.workspace : undefined;
|
||||
return new SettingsManager({ cli, cliCallDelay, defaultSettings }, configuration);
|
||||
}
|
||||
|
||||
let documentSettings = createSettingsManager();
|
||||
const documentInfos = new InfoManager();
|
||||
|
||||
connection.onDidChangeConfiguration((change) => {
|
||||
connection.console.log(`onDidChangeConfiguration event ${JSON.stringify(change)}`);
|
||||
|
||||
documentSettings = createSettingsManager(
|
||||
change.settings.aquaSettings.fluencePath,
|
||||
change.settings.aquaSettings.fluenceCallDelay,
|
||||
change.settings.aquaSettings,
|
||||
);
|
||||
|
||||
// Revalidate all open text documents
|
||||
documents.all().forEach(validateDocument);
|
||||
});
|
||||
|
||||
// Only keep settings for open documents
|
||||
documents.onDidClose((e) => {
|
||||
documentSettings.removeDocumentSettings(e.document.uri);
|
||||
documentInfos.removeDocumentInfo(e.document.uri);
|
||||
});
|
||||
|
||||
connection.onInitialize((params: InitializeParams) => {
|
||||
connection.console.log('onInitialize event');
|
||||
|
||||
const capabilities = params.capabilities;
|
||||
hasConfigurationCapability = !!(capabilities.workspace && !!capabilities.workspace.configuration);
|
||||
hasWorkspaceFolderCapability = !!(capabilities.workspace && !!capabilities.workspace.workspaceFolders);
|
||||
|
||||
if (params.workspaceFolders) {
|
||||
folders = params.workspaceFolders;
|
||||
}
|
||||
|
||||
const result: InitializeResult = {
|
||||
capabilities: {
|
||||
textDocumentSync: TextDocumentSyncKind.Full,
|
||||
definitionProvider: true,
|
||||
hoverProvider: true,
|
||||
},
|
||||
};
|
||||
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
result.capabilities.workspace = {
|
||||
workspaceFolders: {
|
||||
supported: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
connection.onInitialized(async () => {
|
||||
connection.console.log('onInitialized event');
|
||||
|
||||
if (hasConfigurationCapability) {
|
||||
connection.client.register(DidChangeConfigurationNotification.type, {
|
||||
section: 'aquaSettings',
|
||||
});
|
||||
}
|
||||
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
connection.workspace.onDidChangeWorkspaceFolders((event) => {
|
||||
folders = folders.concat(event.added);
|
||||
folders = folders.filter((f) => !event.removed.includes(f));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
documents.onDidSave(async (change) => {
|
||||
connection.console.log('onDidSave event');
|
||||
|
||||
await validateDocument(change.document);
|
||||
});
|
||||
|
||||
documents.onDidOpen(async (change) => {
|
||||
connection.console.log('onDidOpen event');
|
||||
|
||||
await validateDocument(change.document);
|
||||
});
|
||||
|
||||
connection.onHover(({ textDocument, position }: HoverParams): Hover | null => {
|
||||
connection.console.log('onHover event');
|
||||
|
||||
const info = documentInfos.infoAt(textDocument.uri, position);
|
||||
if (info) {
|
||||
const typeStr = typeToString(info.type);
|
||||
const content: MarkupContent = { kind: MarkupKind.PlainText, value: typeStr };
|
||||
return { contents: content };
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
connection.onDefinition(({ textDocument, position }: DefinitionParams) => {
|
||||
connection.console.log('onDefinition event');
|
||||
|
||||
const def = documentInfos.defAt(textDocument.uri, position);
|
||||
|
||||
return def ? [tokenToLocation(def)] : [];
|
||||
});
|
||||
|
||||
async function validateDocument(textDocument: TextDocument): Promise<void> {
|
||||
const settings = await documentSettings.getDocumentSettings(textDocument.uri);
|
||||
|
||||
connection.console.log(`validateDocument ${textDocument.uri} with settings ${JSON.stringify(settings)}`);
|
||||
|
||||
const [diagnostics, info] = await compileAqua(settings, textDocument);
|
||||
|
||||
documentInfos.updateDocumentInfo(textDocument.uri, info);
|
||||
|
||||
// Send the computed diagnostics to VSCode.
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
||||
|
||||
// Request additional imports update if there are errors
|
||||
if (diagnostics.some((d) => d.severity === DiagnosticSeverity.Error)) {
|
||||
documentSettings.requestImportsUpdate(textDocument.uri);
|
||||
}
|
||||
}
|
||||
|
||||
// Make the text document manager listen on the connection
|
||||
// for open, change and close text document events
|
||||
documents.listen(connection);
|
||||
|
||||
// Listen on the connection
|
||||
connection.listen();
|
166
server/src/settings.ts
Normal file
166
server/src/settings.ts
Normal file
@ -0,0 +1,166 @@
|
||||
import type { Configuration } from 'vscode-languageserver/lib/common/configuration';
|
||||
import { URI } from 'vscode-uri';
|
||||
|
||||
import type { Imports } from './imports';
|
||||
import { normalizeImports, uniteImports } from './imports';
|
||||
import type { FluenceCli } from './cli';
|
||||
|
||||
export interface Settings {
|
||||
imports: Imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document info stored in `SettingsManager` cache
|
||||
*/
|
||||
class DocumentSettingsInfo {
|
||||
/* Settings from configuration (or default) */
|
||||
private settings: Settings;
|
||||
/* Additional imports from CLI */
|
||||
private imports: Imports = {};
|
||||
private importsLastUpdated = 0;
|
||||
private importsUpdateRequested = true;
|
||||
|
||||
constructor(settings: Settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
getSettings(): Settings {
|
||||
return {
|
||||
...this.settings,
|
||||
// Imports from settings override imports from CLI
|
||||
imports: uniteImports(this.imports, this.settings.imports),
|
||||
};
|
||||
}
|
||||
|
||||
requestImportsUpdate() {
|
||||
this.importsUpdateRequested = true;
|
||||
}
|
||||
|
||||
isImportsUpdateNeeded(delay?: number): boolean {
|
||||
// Update additional imports not more often than once `delay`
|
||||
// and only if there is a request to update
|
||||
const isUpdateReady = delay ? Date.now() - this.importsLastUpdated > delay : true;
|
||||
return isUpdateReady && this.importsUpdateRequested;
|
||||
}
|
||||
|
||||
updateImports(imports: Imports) {
|
||||
this.imports = imports;
|
||||
this.importsLastUpdated = Date.now();
|
||||
this.importsUpdateRequested = false;
|
||||
}
|
||||
}
|
||||
|
||||
export interface SettingsManagerConfig {
|
||||
cli: FluenceCli;
|
||||
cliCallDelay?: number | undefined;
|
||||
defaultSettings?: Settings | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compilation settings manager for documents
|
||||
*/
|
||||
export class SettingsManager {
|
||||
private readonly defaultSettings: Settings = {
|
||||
imports: {},
|
||||
};
|
||||
private documents: Map<string, DocumentSettingsInfo> = new Map();
|
||||
|
||||
private readonly cli: FluenceCli;
|
||||
private readonly cliCallDelay: number | undefined;
|
||||
private readonly configuration: Configuration | undefined;
|
||||
|
||||
constructor(config: SettingsManagerConfig, configuration?: Configuration) {
|
||||
this.cli = config.cli;
|
||||
this.configuration = configuration;
|
||||
if (config.defaultSettings) {
|
||||
this.defaultSettings = config.defaultSettings;
|
||||
}
|
||||
if (config.cliCallDelay) {
|
||||
this.cliCallDelay = config.cliCallDelay;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings for document or global settings if document settings are not available
|
||||
*
|
||||
* @param uri Document uri
|
||||
* @returns Settings for the document
|
||||
*/
|
||||
async getDocumentSettings(uri: string): Promise<Settings> {
|
||||
const info = await this.updateDocument(uri);
|
||||
|
||||
return info.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove document settings from cache
|
||||
*
|
||||
* @param uri Document uri
|
||||
*/
|
||||
removeDocumentSettings(uri: string): void {
|
||||
this.documents.delete(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flag to request additional imports update.
|
||||
* This flag will be reset after first successful update.
|
||||
* NOTE: Imports are updated no more than once in 5 seconds.
|
||||
*/
|
||||
requestImportsUpdate(uri: string) {
|
||||
this.documents.get(uri)?.requestImportsUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update document info in cache:
|
||||
* 1. Get settings from configuration (or default)
|
||||
* 2. Get additional imports from CLI (if needed)
|
||||
* @param uri document uri
|
||||
* @returns updated document info
|
||||
*/
|
||||
private async updateDocument(uri: string): Promise<DocumentSettingsInfo> {
|
||||
let info = this.documents.get(uri);
|
||||
if (!info) {
|
||||
const settings = await this.getDocumentConfiguration(uri);
|
||||
info = new DocumentSettingsInfo(settings);
|
||||
}
|
||||
|
||||
if (info.isImportsUpdateNeeded(this.cliCallDelay)) {
|
||||
const path = URI.parse(uri).fsPath;
|
||||
try {
|
||||
const imports = await this.cli.imports(path);
|
||||
info.updateImports(imports);
|
||||
} catch (e) {
|
||||
// try-catch is needed, because server will crash if there will be no Fluence CLI installed
|
||||
console.error('Cannot update imports: ', e);
|
||||
}
|
||||
}
|
||||
|
||||
this.documents.set(uri, info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private async getDocumentConfiguration(uri: string): Promise<Settings> {
|
||||
if (this.configuration) {
|
||||
// TODO: Handle errors
|
||||
const settings = await this.configuration.getConfiguration({
|
||||
scopeUri: uri,
|
||||
section: 'aquaSettings',
|
||||
});
|
||||
if (settings) {
|
||||
try {
|
||||
settings.imports = normalizeImports(settings.imports);
|
||||
} catch (e) {
|
||||
// TODO: Maybe show some notification to user?
|
||||
// Don't know how to handle it better.
|
||||
console.error('Cannot normalize imports from settings: ', e);
|
||||
settings.imports = {};
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
return this.defaultSettings;
|
||||
}
|
||||
}
|
30
server/src/test/aqua/simple.aqua
Normal file
30
server/src/test/aqua/simple.aqua
Normal file
@ -0,0 +1,30 @@
|
||||
aqua Simple
|
||||
|
||||
export main
|
||||
|
||||
func getStr() -> string:
|
||||
<- "test string"
|
||||
|
||||
func consumeStr(str: string) -> string:
|
||||
<- str
|
||||
|
||||
service Srv("test-srv"):
|
||||
consumeStr(str: string) -> string
|
||||
|
||||
ability Ab:
|
||||
field: string
|
||||
arrow(s: string) -> string
|
||||
|
||||
func main() -> string:
|
||||
-- Definition
|
||||
testVar <- getStr()
|
||||
-- Use as argument to function
|
||||
newVar1 <- consumeStr(testVar)
|
||||
-- Use as argument to service
|
||||
newVar2 <- Srv.consumeStr(testVar)
|
||||
-- Use as argument to ability creation
|
||||
ab = Ab(field = testVar, arrow = consumeStr)
|
||||
-- Use as argument to ability call
|
||||
newVar3 <- ab.arrow(testVar)
|
||||
-- Use in return statement
|
||||
<- testVar
|
109
server/src/test/imports.test.ts
Normal file
109
server/src/test/imports.test.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { normalizeImports, uniteImports } from '../imports';
|
||||
|
||||
describe('Imports Test Suite', () => {
|
||||
describe('normalizeImports', () => {
|
||||
it('should normalize empty imports', async () => {
|
||||
assert.deepStrictEqual(normalizeImports(undefined), {});
|
||||
assert.deepStrictEqual(normalizeImports(null), {});
|
||||
});
|
||||
|
||||
it('should normalize legacy imports', async () => {
|
||||
const imports = ['a', 'b', 'c'];
|
||||
const normalized = {
|
||||
'/': {
|
||||
'': imports,
|
||||
},
|
||||
};
|
||||
assert.deepStrictEqual(normalizeImports(imports), normalized);
|
||||
});
|
||||
|
||||
it('should normalize imports', async () => {
|
||||
const imports = {
|
||||
'/': {
|
||||
'': ['a', 'b', 'c'],
|
||||
},
|
||||
};
|
||||
assert.deepStrictEqual(normalizeImports(imports), imports);
|
||||
});
|
||||
|
||||
it('should normalize imports with single paths', async () => {
|
||||
const imports = {
|
||||
'/': {
|
||||
'': 'a',
|
||||
},
|
||||
};
|
||||
const normalized = {
|
||||
'/': {
|
||||
'': ['a'],
|
||||
},
|
||||
};
|
||||
assert.deepStrictEqual(normalizeImports(imports), normalized);
|
||||
});
|
||||
|
||||
it('should throw on invalid imports', async () => {
|
||||
assert.throws(() => normalizeImports(123));
|
||||
assert.throws(() => normalizeImports(['a', 123]));
|
||||
assert.throws(() => normalizeImports({ a: 123 }));
|
||||
assert.throws(() => normalizeImports({ a: { b: 123 } }));
|
||||
assert.throws(() => normalizeImports({ a: { b: ['a', 123] } }));
|
||||
});
|
||||
});
|
||||
|
||||
describe('uniteImports', () => {
|
||||
it('should unite distinct imports', async () => {
|
||||
const lhs = {
|
||||
'/left': {
|
||||
'': ['l'],
|
||||
},
|
||||
};
|
||||
const rhs = {
|
||||
'/right': {
|
||||
'': ['r'],
|
||||
},
|
||||
};
|
||||
const result = { ...lhs, ...rhs };
|
||||
assert.deepStrictEqual(uniteImports(lhs, rhs), result);
|
||||
});
|
||||
|
||||
it('should unite path-intersecting imports', async () => {
|
||||
const lhs = {
|
||||
'/': {
|
||||
left: ['l'],
|
||||
},
|
||||
};
|
||||
const rhs = {
|
||||
'/': {
|
||||
right: ['r'],
|
||||
},
|
||||
};
|
||||
const result = {
|
||||
'/': {
|
||||
left: ['l'],
|
||||
right: ['r'],
|
||||
},
|
||||
};
|
||||
assert.deepStrictEqual(uniteImports(lhs, rhs), result);
|
||||
});
|
||||
|
||||
it('should unite path-prefix-intersecting imports', async () => {
|
||||
const lhs = {
|
||||
'/': {
|
||||
'': ['l'],
|
||||
},
|
||||
};
|
||||
const rhs = {
|
||||
'/': {
|
||||
'': ['r'],
|
||||
},
|
||||
};
|
||||
const result = {
|
||||
'/': {
|
||||
'': ['l', 'r'],
|
||||
},
|
||||
};
|
||||
assert.deepStrictEqual(uniteImports(lhs, rhs), result);
|
||||
});
|
||||
});
|
||||
});
|
109
server/src/test/info.test.ts
Normal file
109
server/src/test/info.test.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||
import { Location, Position, Range } from 'vscode-languageserver';
|
||||
|
||||
import { compileAqua } from '../validation';
|
||||
import { DocumentInfo } from '../info';
|
||||
import { pathToUri, tokenToLocation } from '../utils';
|
||||
import { ScalarType } from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
|
||||
|
||||
/**
|
||||
* Load document from file, compile it and return info
|
||||
* @param relPath path relative to test folder
|
||||
* @returns document, document info after compilation
|
||||
*/
|
||||
async function openDocument(relPath: string): Promise<[TextDocument, DocumentInfo]> {
|
||||
const absPath = path.join(__dirname, relPath);
|
||||
|
||||
const content = await fs.readFile(absPath, 'utf-8');
|
||||
const document = TextDocument.create(pathToUri(absPath), 'aqua', 0, content);
|
||||
// Compile without imports
|
||||
const [_, info] = await compileAqua({ imports: {} }, document);
|
||||
|
||||
return [document, info];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all locations of a variable in a document
|
||||
* @param name variable name
|
||||
* @param doc text document to search in
|
||||
* @returns locations of variable in document
|
||||
*/
|
||||
function locationsOf(name: string, doc: TextDocument): Location[] {
|
||||
const regex = new RegExp(`(?<=\\b)${name}(?=\\b)`, 'g');
|
||||
return [...doc.getText().matchAll(regex)].map((match) => {
|
||||
// `index` will always be presented, see
|
||||
// https://github.com/microsoft/TypeScript/issues/36788
|
||||
const index = match.index as number;
|
||||
return {
|
||||
uri: doc.uri,
|
||||
range: {
|
||||
start: doc.positionAt(index),
|
||||
end: doc.positionAt(index + match[0].length),
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all positions in a range
|
||||
* @param doc text document
|
||||
* @param range range
|
||||
* @returns positions in range
|
||||
*/
|
||||
function genRange(doc: TextDocument, range: Range): Position[] {
|
||||
const positions: Position[] = [];
|
||||
|
||||
const begOff = doc.offsetAt(range.start);
|
||||
const endOff = doc.offsetAt(range.end);
|
||||
for (let off = begOff; off < endOff; off++) {
|
||||
positions.push(doc.positionAt(off));
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
describe('DocumentInfo Test Suite', () => {
|
||||
describe('infoAt', () => {
|
||||
it('should return type information on each occurrence (simple)', async () => {
|
||||
const [document, docInfo] = await openDocument('aqua/simple.aqua');
|
||||
const locations = locationsOf('testVar', document);
|
||||
|
||||
assert.strictEqual(locations.length, 6, 'Not all occurrences found');
|
||||
|
||||
for (const loc of locations) {
|
||||
for (const pos of genRange(document, loc.range)) {
|
||||
const info = docInfo.infoAt(pos);
|
||||
|
||||
assert.ok(info, 'Info not found');
|
||||
|
||||
const type = info.type as ScalarType;
|
||||
assert.strictEqual(type.tag, 'scalar', 'Wrong type tag');
|
||||
assert.strictEqual(type.name, 'string', 'Wrong type');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('defAt', () => {
|
||||
it('should return definition location on each occurrence (simple)', async () => {
|
||||
const [document, docInfo] = await openDocument('aqua/simple.aqua');
|
||||
const locations = locationsOf('testVar', document);
|
||||
|
||||
assert.strictEqual(locations.length, 6, 'Not all occurrences found');
|
||||
|
||||
const definition = locations[0];
|
||||
|
||||
for (const loc of locations.slice(1)) {
|
||||
for (const pos of genRange(document, loc.range)) {
|
||||
const def = docInfo.defAt(pos);
|
||||
|
||||
assert.ok(def, 'Definition not found');
|
||||
assert.deepStrictEqual(tokenToLocation(def), definition, 'Wrong definition location');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
81
server/src/types.ts
Normal file
81
server/src/types.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import type {
|
||||
AbilityType,
|
||||
ArrayType,
|
||||
CanonStreamType,
|
||||
OptionType,
|
||||
ServiceType,
|
||||
StreamMapType,
|
||||
StreamType,
|
||||
StructType,
|
||||
Type,
|
||||
} from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
|
||||
|
||||
const collectionPrefix = {
|
||||
array: '[]',
|
||||
stream: '*',
|
||||
canon: '#',
|
||||
streammap: '%',
|
||||
option: '?',
|
||||
};
|
||||
|
||||
function collectionToString(
|
||||
type: ArrayType | StreamType | CanonStreamType | StreamMapType | OptionType,
|
||||
depth: number,
|
||||
) {
|
||||
const elementType = typeToStringInner(type.element, depth);
|
||||
const prefix = collectionPrefix[type.tag];
|
||||
return prefix + elementType;
|
||||
}
|
||||
|
||||
function namedToString(type: AbilityType | StructType | ServiceType, depth: number) {
|
||||
let fieldsStr = Object.entries(type.fields).map(function ([name, fieldType]) {
|
||||
const fieldTypeStr = typeToStringInner(fieldType, depth + 1);
|
||||
return `${name}: ${fieldTypeStr}`;
|
||||
});
|
||||
return `${type.name}(${fieldsStr.join(', ')})`;
|
||||
}
|
||||
|
||||
function typeToStringInner(type: Type, depth: number): string {
|
||||
switch (type.tag) {
|
||||
case 'nil':
|
||||
return '∅';
|
||||
case 'array':
|
||||
case 'option':
|
||||
case 'stream':
|
||||
case 'streammap':
|
||||
case 'canon':
|
||||
return collectionToString(type, depth);
|
||||
case 'struct':
|
||||
case 'ability':
|
||||
case 'service':
|
||||
if (depth >= 1) {
|
||||
return type.name;
|
||||
}
|
||||
return namedToString(type, depth);
|
||||
case 'labeled':
|
||||
const args = Object.entries(type.args).map(function ([name, argType]) {
|
||||
const fieldTypeStr = typeToStringInner(argType, depth + 1);
|
||||
return `${name}: ${fieldTypeStr}`;
|
||||
});
|
||||
return args.join(', ');
|
||||
case 'unlabeled':
|
||||
return type.types.map((t) => typeToStringInner(t, depth + 1)).join(', ');
|
||||
case 'arrow':
|
||||
const domainStr = typeToStringInner(type.domain, depth + 1);
|
||||
const codomainStr = typeToStringInner(type.codomain, depth + 1);
|
||||
return `func (${domainStr}) -> ${codomainStr}`;
|
||||
case 'top':
|
||||
return 'top';
|
||||
case 'bottom':
|
||||
return 'bottom';
|
||||
case 'scalar':
|
||||
return type.name;
|
||||
default:
|
||||
const _exhaustiveCheck: never = type;
|
||||
return _exhaustiveCheck;
|
||||
}
|
||||
}
|
||||
|
||||
export function typeToString(type: Type): string {
|
||||
return typeToStringInner(type, 0);
|
||||
}
|
80
server/src/utils.ts
Normal file
80
server/src/utils.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import url from 'url';
|
||||
|
||||
import type { Position } from 'vscode-languageserver-textdocument';
|
||||
import type { DocumentUri, Location } from 'vscode-languageserver';
|
||||
|
||||
import type { TokenLocation } from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
|
||||
|
||||
/**
|
||||
* Check if position is inside token
|
||||
* @param path path to file with token
|
||||
* @param position position to check
|
||||
* @param token token to check
|
||||
* @returns true if position is inside token
|
||||
* @note this ignores token's file name
|
||||
* as position does not contain such info
|
||||
*/
|
||||
export function posInToken(path: string, position: Position, token: TokenLocation): boolean {
|
||||
return (
|
||||
token.name == path &&
|
||||
token.startLine <= position.line &&
|
||||
token.startCol <= position.character &&
|
||||
token.endLine >= position.line &&
|
||||
token.endCol >= position.character
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if if location is inside other location
|
||||
* @param lhs location to check
|
||||
* @param rhs enclosing location
|
||||
* @returns true if rhs is enclosing lhs
|
||||
*/
|
||||
export function locInLoc(lhs: TokenLocation, rhs: TokenLocation): boolean {
|
||||
return (
|
||||
lhs.name == rhs.name &&
|
||||
rhs.startLine <= lhs.startLine &&
|
||||
rhs.startCol <= lhs.startCol &&
|
||||
rhs.endLine >= lhs.endLine &&
|
||||
rhs.endCol >= lhs.endCol
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert URI to file path
|
||||
* @param uri URI to convert
|
||||
* @returns file path
|
||||
*/
|
||||
export function uriToPath(uri: DocumentUri) {
|
||||
return url.fileURLToPath(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert file path to URI
|
||||
* @param path file path to convert
|
||||
* @returns URI
|
||||
*/
|
||||
export function pathToUri(path: string) {
|
||||
return url.format(url.pathToFileURL(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert token location to location
|
||||
* @param loc token location
|
||||
* @returns location
|
||||
*/
|
||||
export function tokenToLocation(loc: TokenLocation): Location {
|
||||
return {
|
||||
uri: pathToUri(loc.name),
|
||||
range: {
|
||||
start: {
|
||||
line: loc.startLine,
|
||||
character: loc.startCol,
|
||||
},
|
||||
end: {
|
||||
line: loc.endLine,
|
||||
character: loc.endCol,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
107
server/src/validation.ts
Normal file
107
server/src/validation.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import * as fs from 'fs';
|
||||
import * as Path from 'path';
|
||||
|
||||
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
||||
import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver/node';
|
||||
|
||||
import { AquaLSP, ErrorInfo, TokenLink, WarningInfo } from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
|
||||
|
||||
import type { Settings } from './settings';
|
||||
import { DocumentInfo } from './info';
|
||||
import { uriToPath } from './utils';
|
||||
|
||||
function infoToDiagnostic(textDocument: TextDocument, info: ErrorInfo | WarningInfo): Diagnostic {
|
||||
const severity = info.infoType === 'error' ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning;
|
||||
|
||||
const diagnostic: Diagnostic = {
|
||||
severity: severity,
|
||||
range: {
|
||||
start: textDocument.positionAt(info.start),
|
||||
end: textDocument.positionAt(info.end),
|
||||
},
|
||||
message: info.message,
|
||||
};
|
||||
|
||||
if (info.location) {
|
||||
const message = info.infoType === 'error' ? 'Compilation error' : 'Compilation warning';
|
||||
|
||||
diagnostic.relatedInformation = [
|
||||
{
|
||||
location: {
|
||||
uri: info.location,
|
||||
range: Object.assign({}, diagnostic.range),
|
||||
},
|
||||
message: message,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return diagnostic;
|
||||
}
|
||||
|
||||
export async function compileAqua(
|
||||
settings: Settings,
|
||||
textDocument: TextDocument,
|
||||
): Promise<[Diagnostic[], DocumentInfo]> {
|
||||
const path = uriToPath(textDocument.uri);
|
||||
|
||||
// compile aqua and get result
|
||||
const result = await AquaLSP.compile(path, settings.imports);
|
||||
|
||||
const diagnostics: Diagnostic[] = [];
|
||||
const links: TokenLink[] = [];
|
||||
|
||||
const docPath = Path.parse(path);
|
||||
|
||||
// empty string for already resolved paths in 'importLocations'
|
||||
const linksSearch = ['', docPath.dir];
|
||||
|
||||
result.importLocations.map(function (ti) {
|
||||
// add extension for imports without extension
|
||||
let importPath: string;
|
||||
if (ti.path.endsWith('.aqua')) {
|
||||
importPath = ti.path;
|
||||
} else {
|
||||
importPath = ti.path + '.aqua';
|
||||
}
|
||||
|
||||
const path = linksSearch.map((i) => Path.join(i, importPath)).find(fs.existsSync);
|
||||
|
||||
|
||||
if (path) {
|
||||
links.push({
|
||||
current: ti.current,
|
||||
definition: {
|
||||
name: path,
|
||||
startLine: 0,
|
||||
startCol: 0,
|
||||
endLine: 0,
|
||||
endCol: 0,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
console.log(`Cannot find path for '${ti}'`)
|
||||
}
|
||||
});
|
||||
|
||||
if (result.warnings) {
|
||||
// Add all warnings related to the file to Diagnostic
|
||||
const warnings = result.warnings
|
||||
.filter((w) => w.location == null || w.location === path)
|
||||
.map((w) => infoToDiagnostic(textDocument, w));
|
||||
diagnostics.push(...warnings);
|
||||
}
|
||||
|
||||
if (result.errors) {
|
||||
// Add all errors related to the file to Diagnostic
|
||||
const errors = result.errors
|
||||
.filter((e) => e.location == null || e.location === path)
|
||||
.map((e) => infoToDiagnostic(textDocument, e));
|
||||
diagnostics.push(...errors);
|
||||
}
|
||||
|
||||
const locations = result.locations.concat(links);
|
||||
const info = new DocumentInfo(path, locations, result.tokens);
|
||||
|
||||
return [diagnostics, info];
|
||||
}
|
15
server/tsconfig.json
Normal file
15
server/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"allowJs": true,
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"outDir": "out",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "src/test"],
|
||||
"extends": "@tsconfig/node16-strictest/tsconfig.json"
|
||||
}
|
100
syntaxes/air.tmLanguage.json
Normal file
100
syntaxes/air.tmLanguage.json
Normal file
@ -0,0 +1,100 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||
"name": "air",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#keywords"
|
||||
},
|
||||
{
|
||||
"include": "#constants"
|
||||
},
|
||||
{
|
||||
"include": "#strings"
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"include": "#variables"
|
||||
},
|
||||
{
|
||||
"include": "#lambda"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"keywords": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.instruction.air",
|
||||
"match": "\\b(ap|call|canon|fold|par|next|match|mismatch|new|null|seq|xor)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"constants": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.language.air",
|
||||
"match": "%init_peer_id%|%last_error%"
|
||||
},
|
||||
{
|
||||
"name": "constant.numeric.air",
|
||||
"match": "\\b\\d+\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.boolean.air",
|
||||
"match": "\\b(true|false)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"strings": {
|
||||
"name": "string.quoted.double.air",
|
||||
"begin": "\"",
|
||||
"end": "\"",
|
||||
"patterns": []
|
||||
},
|
||||
"comments": {
|
||||
"name": "comment.line.air",
|
||||
"begin": ";",
|
||||
"end": "$"
|
||||
},
|
||||
"lambda": {
|
||||
"//": "AIR lambdas",
|
||||
"begin": "\\.\\$",
|
||||
"end": "!",
|
||||
"beginCaptures": {
|
||||
"0": { "name": "punctuation.paren.open" }
|
||||
},
|
||||
"endCaptures": {
|
||||
"0": { "name": "punctuation.paren.close" }
|
||||
},
|
||||
"name": "expression.group",
|
||||
"patterns": [
|
||||
{
|
||||
"//": "Field accessor",
|
||||
"name": "support.function.method.call",
|
||||
"match": "\\.\\w+"
|
||||
},
|
||||
{
|
||||
"//": "Array acessor",
|
||||
"name": "keyword.control.other",
|
||||
"match": "\\[[A-Za-z0-9\\-_\\\"]+\\]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"variables": {
|
||||
"patterns": [
|
||||
{
|
||||
"//": "Stream and canonicalized stream variables start with $ or # respectively",
|
||||
"name": "variable.language.stream",
|
||||
"match": "[\\$\\#][A-Za-z_\\-][A-Za-z0-9_\\-]*"
|
||||
},
|
||||
{
|
||||
"//": "Scalar variables",
|
||||
"name": "variable.other.scalar",
|
||||
"match": "[A-Za-z_\\-][A-Za-z0-9_\\-]*"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scopeName": "source.air"
|
||||
}
|
196
syntaxes/aqua.tmLanguage.json
Normal file
196
syntaxes/aqua.tmLanguage.json
Normal file
@ -0,0 +1,196 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||
"name": "aqua",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"include": "#keywords"
|
||||
},
|
||||
{
|
||||
"include": "#constants"
|
||||
},
|
||||
{
|
||||
"include": "#strings"
|
||||
},
|
||||
{
|
||||
"include": "#semantics"
|
||||
},
|
||||
{
|
||||
"include": "#todo-keyword"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"keywords": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.control.flow.aqua",
|
||||
"match": "\\b(try|catch|par|if|else|otherwise|for|co|join|parseq)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.control.other.aqua",
|
||||
"match": "(<<-|<-|->)"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.math.aqua",
|
||||
"match": "(\\+|-|\/|(?<=[^:]\\s*)\\*|%|\\*\\*|>=|<=|>|<)"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.logical.aqua",
|
||||
"match": "=="
|
||||
},
|
||||
{
|
||||
"name": "keyword.control.other.aqua",
|
||||
"match": "(\\[\\]|\\*|\\?|=|\\?=)"
|
||||
},
|
||||
{
|
||||
"name": "keyword.control.topology.aqua",
|
||||
"match": "\\b(on|via)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.control.declaration.aqua",
|
||||
"match": "\\b(func|service|data|ability|alias|const)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.logical.aqua",
|
||||
"match": "(\\|\\||&&|!(?=\\s*\\w))"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.control.aqua",
|
||||
"match": "(?<=\\w\\s*)!"
|
||||
},
|
||||
{
|
||||
"name": "keyword.control.import.aqua",
|
||||
"match": "\\b(import|module|export|declares|from|as|use|aqua)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"constants": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.language.topology.aqua",
|
||||
"match": "%init_peer_id%|%last_error%|%HOST_PEER_ID%|HOST_PEER_ID|INIT_PEER_ID"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.numeric.aqua",
|
||||
"match": "\\b-?\\d+(\\.\\d*)?\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.boolean.aqua",
|
||||
"match": "\\b(true|false)\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.option.nil.aqua",
|
||||
"match": "\\bnil\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"strings": {
|
||||
"name": "string.quoted.double.aqua",
|
||||
"begin": "\"",
|
||||
"end": "\"",
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.character.escape.aqua",
|
||||
"match": "\\\\."
|
||||
}
|
||||
]
|
||||
},
|
||||
"comments": {
|
||||
"name": "comment.line.aqua",
|
||||
"begin": "--",
|
||||
"end": "$",
|
||||
"patterns": [{ "include": "#todo-keyword" }]
|
||||
},
|
||||
"todo-keyword": {
|
||||
"begin": "TODO:",
|
||||
"end": "$",
|
||||
"name": "keyword.todo"
|
||||
},
|
||||
"semantics": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "entity.name.type.primitive.aqua",
|
||||
"match": "\\b(string|bool|u8|u16|u32|u64|i8|i16|i32|i64|f32|f64)\\b"
|
||||
},
|
||||
{
|
||||
"//": "Matches type name defined with alias clause",
|
||||
"name": "entity.name.type.aqua",
|
||||
"match": "(?<=alias\\s*)[A-Z]\\w*(?=\\s*:)"
|
||||
},
|
||||
{
|
||||
"//": "Matches type specifiers in declarations",
|
||||
"name": "entity.name.type.specifier.aqua",
|
||||
"match": "(?<=:\\s*)(?:\\[\\]|\\*|\\?)*[A-Za-z]\\w*"
|
||||
},
|
||||
{
|
||||
"//": "Defines tokens for data definitions ('data XXX:')",
|
||||
"name": "entity.name.type.data.aqua",
|
||||
"match": "\\b(?<=(data)\\s+)[A-Z][A-Za-z0-9_]+(?=:)\\b"
|
||||
},
|
||||
{
|
||||
"//": "Defines tokens for service definitions ('service YYY:')",
|
||||
"name": "entity.name.type.service.aqua",
|
||||
"match": "\\b(?<=(service)\\s+)[A-Z][A-Za-z0-9_]+(?=(\\(\"\\w*\"\\))?:)\\b"
|
||||
},
|
||||
{
|
||||
"//": "Defines tokens for ability definitions ('ability YYY:')",
|
||||
"name": "entity.name.type.ability.aqua",
|
||||
"match": "\\b(?<=(ability)\\s+)[A-Z][A-Za-z0-9_]+(?=:)\\b"
|
||||
},
|
||||
{
|
||||
"//": "Method name in 'Ability.field.method(args)'",
|
||||
"name": "support.function.method.call.aqua",
|
||||
"match": "(?<=\\.)[a-z][a-zA-Z0-9_]+(?=\\()"
|
||||
},
|
||||
{
|
||||
"//": "Ability name in 'Ability.method(args)' or 'Ability.field'",
|
||||
"name": "support.type.ability.aqua",
|
||||
"match": "\\b(?<=[^\\.])[A-Za-z]\\w*(?=\\.)"
|
||||
},
|
||||
{
|
||||
"//": "Field name in 'Ability.field.method(args)' or 'Ability.field'",
|
||||
"name": "support.other.field.aqua",
|
||||
"match": "(?<=\\.)(?>[A-Za-z]\\w*)\\b(?!\\s*\\()"
|
||||
},
|
||||
{
|
||||
"//": "Matches function arguments",
|
||||
"name": "variable.parameter.value.aqua",
|
||||
"match": "(?<=[,\\(]\\s*)[A-Za-z]\\w*(?=\\s*:)"
|
||||
},
|
||||
{
|
||||
"//": "Matches function ability arguments",
|
||||
"name": "variable.parameter.ability.aqua",
|
||||
"match": "(?<=func\\s*[A-Za-z]\\w*\\s*{\\s*(?:[A-Za-z]\\w*\\s*,\\s*)*)[A-Za-z]\\w*\\b"
|
||||
},
|
||||
{
|
||||
"//": "Matches defined variables",
|
||||
"name": "variable.other.definition.aqua",
|
||||
"match": "\\b[A-Za-z]\\w*(?=\\s*(?:,\\s*[A-Za-z]\\w*)*\\s*(?:<-|=))"
|
||||
},
|
||||
{
|
||||
"//": "Matches declared variables",
|
||||
"name": "variable.other.declaration.aqua",
|
||||
"match": "\\b[A-Za-z]\\w*(?=:)"
|
||||
},
|
||||
{
|
||||
"//": "Matches function definition",
|
||||
"name": "entity.name.function.aqua",
|
||||
"match": "\\b(?<=func\\s*)[A-Za-z]\\w*(?=\\s*(?:\\(|\\{))"
|
||||
},
|
||||
{
|
||||
"//": "Matches method definition, function usage or aggregate creation",
|
||||
"name": "entity.name.function.usage.aqua",
|
||||
"match": "\\b[A-Za-z]\\w*(?=\\s*(?:\\(|\\{))"
|
||||
},
|
||||
{
|
||||
"//": "Matches variables anywhere",
|
||||
"name": "variable.other.any.aqua",
|
||||
"match": "\\b[A-Za-z]\\w*\\b"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scopeName": "source.aqua"
|
||||
}
|
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"allowJs": true,
|
||||
"declaration": true,
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2020",
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
"sourceMap": true
|
||||
},
|
||||
"types": ["node"],
|
||||
"include": [],
|
||||
"exclude": ["node_modules", ".vscode-test"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./client"
|
||||
},
|
||||
{
|
||||
"path": "./server"
|
||||
},
|
||||
{
|
||||
"path": "./integration-tests"
|
||||
}
|
||||
],
|
||||
"extends": "@tsconfig/node16-strictest/tsconfig.json"
|
||||
}
|
3
vscode-highlight/.gitignore
vendored
3
vscode-highlight/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
node_modules
|
||||
*.vsix
|
||||
*.tgz
|
@ -1,65 +0,0 @@
|
||||
# aqua README
|
||||
|
||||
This is the README for your extension "aqua". After writing up a brief description, we recommend including the following sections.
|
||||
|
||||
## Features
|
||||
|
||||
Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file.
|
||||
|
||||
For example if there is an image subfolder under your extension project workspace:
|
||||
|
||||
\!\[feature X\]\(images/feature-x.png\)
|
||||
|
||||
> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow.
|
||||
|
||||
## Requirements
|
||||
|
||||
If you have any requirements or dependencies, add a section describing those and how to install and configure them.
|
||||
|
||||
## Extension Settings
|
||||
|
||||
Include if your extension adds any VS Code settings through the `contributes.configuration` extension point.
|
||||
|
||||
For example:
|
||||
|
||||
This extension contributes the following settings:
|
||||
|
||||
* `myExtension.enable`: enable/disable this extension
|
||||
* `myExtension.thing`: set to `blah` to do something
|
||||
|
||||
## Known Issues
|
||||
|
||||
Calling out known issues can help limit users opening duplicate issues against your extension.
|
||||
|
||||
## Release Notes
|
||||
|
||||
Users appreciate release notes as you update your extension.
|
||||
|
||||
### 1.0.0
|
||||
|
||||
Initial release of ...
|
||||
|
||||
### 1.0.1
|
||||
|
||||
Fixed issue #.
|
||||
|
||||
### 1.1.0
|
||||
|
||||
Added features X, Y, and Z.
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------
|
||||
|
||||
## Working with Markdown
|
||||
|
||||
**Note:** You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts:
|
||||
|
||||
* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux)
|
||||
* Toggle preview (`Shift+CMD+V` on macOS or `Shift+Ctrl+V` on Windows and Linux)
|
||||
* Press `Ctrl+Space` (Windows, Linux) or `Cmd+Space` (macOS) to see a list of Markdown snippets
|
||||
|
||||
### For more information
|
||||
|
||||
* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown)
|
||||
* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/)
|
||||
|
||||
**Enjoy!**
|
@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "aqua-syntax-highlight",
|
||||
"displayName": "aqua",
|
||||
"description": "aqua language support",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"vscode": "^1.56.0"
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages"
|
||||
],
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "aqua",
|
||||
"aliases": [
|
||||
"aqua"
|
||||
],
|
||||
"extensions": [
|
||||
".aqua"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "aqua",
|
||||
"scopeName": "source.aqua",
|
||||
"path": "./syntaxes/aqua.tmLanguage.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||
"name": "aqua",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#keywords"
|
||||
},
|
||||
{
|
||||
"include": "#strings"
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"include": "#semantics"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"keywords": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.control.aqua",
|
||||
"match": "\\b(if|else|otherwise|use|try|catch|on|via|for|par|use|<-)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.logical.aqua",
|
||||
"match": "\\b(eqs|neq)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.declaration.aqua",
|
||||
"match": "\\b(func|service|data|alias)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.control.import",
|
||||
"match": "\\b(import)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"strings": {
|
||||
"name": "string.quoted.double.aqua",
|
||||
"begin": "\"",
|
||||
"end": "\"",
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.character.escape.aqua",
|
||||
"match": "\\\\."
|
||||
}
|
||||
]
|
||||
},
|
||||
"comments": {
|
||||
"name": "comment.line.aqua",
|
||||
"begin": "--",
|
||||
"end": "$"
|
||||
},
|
||||
"semantics": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "storage.type.aqua",
|
||||
"match": "\\b(string|bool|u8|u16|u32|u64|s8|s16|s32|s64|f32|f64)\\b"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scopeName": "source.aqua"
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
# Welcome to your VS Code Extension
|
||||
|
||||
## What's in the folder
|
||||
|
||||
* This folder contains all of the files necessary for your extension.
|
||||
* `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension.
|
||||
* `syntaxes/aqua.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization.
|
||||
* `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets.
|
||||
|
||||
## Get up and running straight away
|
||||
|
||||
* Make sure the language configuration settings in `language-configuration.json` are accurate.
|
||||
* Press `F5` to open a new window with your extension loaded.
|
||||
* Create a new file with a file name suffix matching your language.
|
||||
* Verify that syntax highlighting works and that the language configuration settings are working.
|
||||
|
||||
## Make changes
|
||||
|
||||
* You can relaunch the extension from the debug toolbar after making changes to the files listed above.
|
||||
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
|
||||
|
||||
## Add more language features
|
||||
|
||||
* To add features such as intellisense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/docs
|
||||
|
||||
## Install your extension
|
||||
|
||||
* To start using your extension with Visual Studio Code copy it into the `<user home>/.vscode/extensions` folder and restart Code.
|
||||
* To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension.
|
Loading…
x
Reference in New Issue
Block a user