Move lsp server to this repo (#5619)
This commit is contained in:
@ -1,4 +1,6 @@
|
|||||||
rust/*
|
rust/**/*.ts
|
||||||
|
!rust/kcl-language-server/client/src/**/*.ts
|
||||||
*.typegen.ts
|
*.typegen.ts
|
||||||
packages/codemirror-lsp-client/dist/*
|
packages/codemirror-lsp-client/dist/*
|
||||||
e2e/playwright/snapshots/prompt-to-edit/*
|
e2e/playwright/snapshots/prompt-to-edit/*
|
||||||
|
.vscode-test
|
||||||
|
8
.github/actions/github-release/Dockerfile
vendored
Normal file
8
.github/actions/github-release/Dockerfile
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
FROM node:slim
|
||||||
|
|
||||||
|
COPY . /action
|
||||||
|
WORKDIR /action
|
||||||
|
|
||||||
|
RUN npm install --production
|
||||||
|
|
||||||
|
ENTRYPOINT ["node", "/action/main.js"]
|
21
.github/actions/github-release/README.md
vendored
Normal file
21
.github/actions/github-release/README.md
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# github-release
|
||||||
|
|
||||||
|
Copy-pasted from
|
||||||
|
https://github.com/bytecodealliance/wasmtime/tree/8acfdbdd8aa550d1b84e0ce1e6222a6605d14e38/.github/actions/github-release
|
||||||
|
|
||||||
|
An action used to publish GitHub releases for `wasmtime`.
|
||||||
|
|
||||||
|
As of the time of this writing there's a few actions floating around which
|
||||||
|
perform github releases but they all tend to have their set of drawbacks.
|
||||||
|
Additionally nothing handles deleting releases which we need for our rolling
|
||||||
|
`dev` release.
|
||||||
|
|
||||||
|
To handle all this, this action rolls its own implementation using the
|
||||||
|
actions/toolkit repository and packages published there. These run in a Docker
|
||||||
|
container and take various inputs to orchestrate the release from the build.
|
||||||
|
|
||||||
|
More comments can be found in `main.js`.
|
||||||
|
|
||||||
|
Testing this is really hard. If you want to try though run `npm install` and
|
||||||
|
then `node main.js`. You'll have to configure a bunch of env vars though to get
|
||||||
|
anything reasonably working.
|
15
.github/actions/github-release/action.yml
vendored
Normal file
15
.github/actions/github-release/action.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
name: "wasmtime github releases"
|
||||||
|
description: "wasmtime github releases"
|
||||||
|
inputs:
|
||||||
|
token:
|
||||||
|
description: ""
|
||||||
|
required: true
|
||||||
|
name:
|
||||||
|
description: ""
|
||||||
|
required: true
|
||||||
|
files:
|
||||||
|
description: ""
|
||||||
|
required: true
|
||||||
|
runs:
|
||||||
|
using: "docker"
|
||||||
|
image: "Dockerfile"
|
143
.github/actions/github-release/main.js
vendored
Normal file
143
.github/actions/github-release/main.js
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
const core = require("@actions/core");
|
||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
const github = require("@actions/github");
|
||||||
|
const glob = require("glob");
|
||||||
|
|
||||||
|
function sleep(milliseconds) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runOnce() {
|
||||||
|
// Load all our inputs and env vars. Note that `getInput` reads from `INPUT_*`
|
||||||
|
const files = core.getInput("files");
|
||||||
|
const name = core.getInput("name");
|
||||||
|
const token = core.getInput("token");
|
||||||
|
const slug = process.env.GITHUB_REPOSITORY;
|
||||||
|
const owner = slug.split("/")[0];
|
||||||
|
const repo = slug.split("/")[1];
|
||||||
|
const sha = process.env.HEAD_SHA;
|
||||||
|
|
||||||
|
core.info(`files: ${files}`);
|
||||||
|
core.info(`name: ${name}`);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
request: {
|
||||||
|
timeout: 30000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const octokit = github.getOctokit(token, options);
|
||||||
|
|
||||||
|
// Delete the previous release since we can't overwrite one. This may happen
|
||||||
|
// due to retrying an upload or it may happen because we're doing the dev
|
||||||
|
// release.
|
||||||
|
const releases = await octokit.paginate("GET /repos/:owner/:repo/releases", { owner, repo });
|
||||||
|
for (const release of releases) {
|
||||||
|
if (release.tag_name !== name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const release_id = release.id;
|
||||||
|
core.info(`deleting release ${release_id}`);
|
||||||
|
await octokit.rest.repos.deleteRelease({ owner, repo, release_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// We also need to update the `dev` tag while we're at it on the `dev` branch.
|
||||||
|
if (name == "nightly") {
|
||||||
|
try {
|
||||||
|
core.info(`updating nightly tag`);
|
||||||
|
await octokit.rest.git.updateRef({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
ref: "tags/nightly",
|
||||||
|
sha,
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
core.error(e);
|
||||||
|
core.info(`creating nightly tag`);
|
||||||
|
await octokit.rest.git.createTag({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
tag: "nightly",
|
||||||
|
message: "nightly release",
|
||||||
|
object: sha,
|
||||||
|
type: "commit",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an official GitHub release for this `tag`, and if this is `dev`
|
||||||
|
// then we know that from the previous block this should be a fresh release.
|
||||||
|
core.info(`creating a release`);
|
||||||
|
const release = await octokit.rest.repos.createRelease({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
name,
|
||||||
|
tag_name: name,
|
||||||
|
target_commitish: sha,
|
||||||
|
prerelease: name === "nightly",
|
||||||
|
});
|
||||||
|
const release_id = release.data.id;
|
||||||
|
|
||||||
|
// Upload all the relevant assets for this release as just general blobs.
|
||||||
|
for (const file of glob.sync(files)) {
|
||||||
|
const size = fs.statSync(file).size;
|
||||||
|
const name = path.basename(file);
|
||||||
|
|
||||||
|
await runWithRetry(async function () {
|
||||||
|
// We can't overwrite assets, so remove existing ones from a previous try.
|
||||||
|
let assets = await octokit.rest.repos.listReleaseAssets({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
release_id,
|
||||||
|
});
|
||||||
|
for (const asset of assets.data) {
|
||||||
|
if (asset.name === name) {
|
||||||
|
core.info(`delete asset ${name}`);
|
||||||
|
const asset_id = asset.id;
|
||||||
|
await octokit.rest.repos.deleteReleaseAsset({ owner, repo, asset_id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`upload ${file}`);
|
||||||
|
const headers = { "content-length": size, "content-type": "application/octet-stream" };
|
||||||
|
const data = fs.createReadStream(file);
|
||||||
|
await octokit.rest.repos.uploadReleaseAsset({
|
||||||
|
data,
|
||||||
|
headers,
|
||||||
|
name,
|
||||||
|
url: release.data.upload_url,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runWithRetry(f) {
|
||||||
|
const retries = 10;
|
||||||
|
const maxDelay = 4000;
|
||||||
|
let delay = 1000;
|
||||||
|
|
||||||
|
for (let i = 0; i < retries; i++) {
|
||||||
|
try {
|
||||||
|
await f();
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
if (i === retries - 1) throw e;
|
||||||
|
|
||||||
|
core.error(e);
|
||||||
|
const currentDelay = Math.round(Math.random() * delay);
|
||||||
|
core.info(`sleeping ${currentDelay} ms`);
|
||||||
|
await sleep(currentDelay);
|
||||||
|
delay = Math.min(delay * 2, maxDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
await runWithRetry(runOnce);
|
||||||
|
}
|
||||||
|
|
||||||
|
run().catch((err) => {
|
||||||
|
core.error(err);
|
||||||
|
core.setFailed(err.message);
|
||||||
|
});
|
10
.github/actions/github-release/package.json
vendored
Normal file
10
.github/actions/github-release/package.json
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "wasmtime-github-release",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "main.js",
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.6",
|
||||||
|
"@actions/github": "^5.0",
|
||||||
|
"glob": "^7.1.5"
|
||||||
|
}
|
||||||
|
}
|
1
.github/dependabot.yml
vendored
1
.github/dependabot.yml
vendored
@ -10,6 +10,7 @@ updates:
|
|||||||
- '/'
|
- '/'
|
||||||
- '/packages/codemirror-lang-kcl/'
|
- '/packages/codemirror-lang-kcl/'
|
||||||
- '/packages/codemirror-lsp-client/'
|
- '/packages/codemirror-lsp-client/'
|
||||||
|
- '/rust/kcl-language-server/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
day: monday
|
day: monday
|
||||||
|
401
.github/workflows/kcl-language-server.yml
vendored
Normal file
401
.github/workflows/kcl-language-server.yml
vendored
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
name: kcl-language-server
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- '**/Cargo.toml'
|
||||||
|
- '**/Cargo.lock'
|
||||||
|
- '**/rust-toolchain.toml'
|
||||||
|
- 'rust/kcl-language-server/**'
|
||||||
|
- '**.rs'
|
||||||
|
- .github/workflows/kcl-language-server.yml
|
||||||
|
tags:
|
||||||
|
- 'kcl-*'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '**/Cargo.toml'
|
||||||
|
- '**/Cargo.lock'
|
||||||
|
- '**/rust-toolchain.toml'
|
||||||
|
- 'rust/kcl-language-server/**'
|
||||||
|
- '**.rs'
|
||||||
|
- .github/workflows/kcl-language-server.yml
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
CARGO_NET_RETRY: 10
|
||||||
|
RUSTFLAGS: ""
|
||||||
|
RUSTUP_MAX_RETRIES: 10
|
||||||
|
FETCH_DEPTH: 0 # pull in the tags for the version string
|
||||||
|
MACOSX_DEPLOYMENT_TARGET: 10.15
|
||||||
|
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
||||||
|
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: vscode tests
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: ".nvmrc"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
yarn install
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
yarn install
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
yarn build
|
||||||
|
yarn test-compile
|
||||||
|
ls -la dist
|
||||||
|
xvfb-run -a yarn test
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
yarn test
|
||||||
|
if: runner.os != 'Linux'
|
||||||
|
build-release:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
code-target:
|
||||||
|
win32-x64
|
||||||
|
#- os: windows-latest
|
||||||
|
#target: i686-pc-windows-msvc
|
||||||
|
#code-target:
|
||||||
|
#win32-ia32
|
||||||
|
#- os: windows-latest
|
||||||
|
#target: aarch64-pc-windows-msvc
|
||||||
|
#code-target: win32-arm64
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
code-target:
|
||||||
|
linux-x64
|
||||||
|
#- os: ubuntu-latest
|
||||||
|
#target: aarch64-unknown-linux-musl
|
||||||
|
#code-target: linux-arm64
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: aarch64-unknown-linux-gnu
|
||||||
|
code-target: linux-arm64
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: arm-unknown-linux-gnueabihf
|
||||||
|
code-target: linux-armhf
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
code-target: darwin-x64
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
code-target: darwin-arm64
|
||||||
|
|
||||||
|
name: build-release (${{ matrix.target }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
container: ${{ matrix.container }}
|
||||||
|
|
||||||
|
env:
|
||||||
|
RA_TARGET: ${{ matrix.target }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: ${{ env.FETCH_DEPTH }}
|
||||||
|
|
||||||
|
- name: Use correct Rust toolchain
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
rm rust/rust-toolchain.toml
|
||||||
|
|
||||||
|
- name: Install rust
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
cache: rust
|
||||||
|
components: rust-src
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: ".nvmrc"
|
||||||
|
|
||||||
|
- name: Update apt repositories
|
||||||
|
if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' || matrix.os == 'ubuntu-latest'
|
||||||
|
run: sudo apt-get update
|
||||||
|
|
||||||
|
- if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||||
|
name: Install deps
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo apt install -y \
|
||||||
|
ca-certificates \
|
||||||
|
clang \
|
||||||
|
cmake \
|
||||||
|
curl \
|
||||||
|
g++ \
|
||||||
|
gcc \
|
||||||
|
gcc-mingw-w64-i686 \
|
||||||
|
gcc-mingw-w64 \
|
||||||
|
jq \
|
||||||
|
libmpc-dev \
|
||||||
|
libmpfr-dev \
|
||||||
|
libgmp-dev \
|
||||||
|
libssl-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
mingw-w64 \
|
||||||
|
wget \
|
||||||
|
zlib1g-dev
|
||||||
|
|
||||||
|
cargo install cross
|
||||||
|
|
||||||
|
- name: Install AArch64 target toolchain
|
||||||
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||||
|
run: sudo apt-get install gcc-aarch64-linux-gnu
|
||||||
|
|
||||||
|
- name: Install ARM target toolchain
|
||||||
|
if: matrix.target == 'arm-unknown-linux-gnueabihf'
|
||||||
|
run: sudo apt-get install gcc-arm-linux-gnueabihf
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cd rust
|
||||||
|
cargo kcl-language-server-release build --client-patch-version ${{ github.run_number }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
- name: Package Extension (release)
|
||||||
|
if: startsWith(github.event.ref, 'refs/tags/')
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
npx vsce package -o "../build/kcl-language-server-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }}
|
||||||
|
|
||||||
|
- name: Package Extension (nightly)
|
||||||
|
if: startsWith(github.event.ref, 'refs/tags/') == false
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
npx vsce package -o "../build/kcl-language-server-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release
|
||||||
|
|
||||||
|
- name: remove server
|
||||||
|
if: matrix.target == 'x86_64-unknown-linux-gnu'
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
rm -rf server
|
||||||
|
|
||||||
|
- name: Package Extension (no server, release)
|
||||||
|
if: matrix.target == 'x86_64-unknown-linux-gnu' && startsWith(github.event.ref, 'refs/tags/')
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
npx vsce package -o ../build/kcl-language-server-no-server.vsix
|
||||||
|
|
||||||
|
- name: Package Extension (no server, nightly)
|
||||||
|
if: matrix.target == 'x86_64-unknown-linux-gnu' && startsWith(github.event.ref, 'refs/tags/') == false
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
npx vsce package -o ../build/kcl-language-server-no-server.vsix --pre-release
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-${{ matrix.target }}
|
||||||
|
path: ./rust/build
|
||||||
|
|
||||||
|
build-release-x86_64-unknown-linux-musl:
|
||||||
|
name: build-release (x86_64-unknown-linux-musl)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
RA_TARGET: x86_64-unknown-linux-musl
|
||||||
|
# For some reason `-crt-static` is not working for clang without lld
|
||||||
|
RUSTFLAGS: "-C link-arg=-fuse-ld=lld -C target-feature=-crt-static"
|
||||||
|
container:
|
||||||
|
image: alpine:latest
|
||||||
|
volumes:
|
||||||
|
- /usr/local/cargo/registry:/usr/local/cargo/registry
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apk add --no-cache \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
clang \
|
||||||
|
lld \
|
||||||
|
musl-dev \
|
||||||
|
nodejs \
|
||||||
|
yarn \
|
||||||
|
npm
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: ${{ env.FETCH_DEPTH }}
|
||||||
|
|
||||||
|
- name: Use correct Rust toolchain
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
rm rust/rust-toolchain.toml
|
||||||
|
|
||||||
|
- name: Install rust
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
cache: rust
|
||||||
|
components: rust-src
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cd rust
|
||||||
|
cargo kcl-language-server-release build --client-patch-version ${{ github.run_number }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
- name: Package Extension (release)
|
||||||
|
if: startsWith(github.event.ref, 'refs/tags/')
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
npx vsce package -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64
|
||||||
|
|
||||||
|
- name: Package Extension (release)
|
||||||
|
if: startsWith(github.event.ref, 'refs/tags/') == false
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
npx vsce package -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64 --pre-release
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-x86_64-unknown-linux-musl
|
||||||
|
path: ./rust/build
|
||||||
|
|
||||||
|
publish:
|
||||||
|
name: publish
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: ["build-release", "build-release-x86_64-unknown-linux-musl"]
|
||||||
|
if: startsWith(github.event.ref, 'refs/tags')
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- run: 'echo "TAG: $TAG"'
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: ${{ env.FETCH_DEPTH }}
|
||||||
|
|
||||||
|
- name: Install Nodejs
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: ".nvmrc"
|
||||||
|
|
||||||
|
- run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||||
|
- run: 'echo "HEAD_SHA: $HEAD_SHA"'
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-aarch64-apple-darwin
|
||||||
|
path: rust/build
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-x86_64-apple-darwin
|
||||||
|
path: rust/build
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-x86_64-unknown-linux-gnu
|
||||||
|
path: rust/build
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-x86_64-unknown-linux-musl
|
||||||
|
path: rust/build
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-aarch64-unknown-linux-gnu
|
||||||
|
path: rust/build
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-arm-unknown-linux-gnueabihf
|
||||||
|
path: rust/build
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-x86_64-pc-windows-msvc
|
||||||
|
path:
|
||||||
|
rust/build
|
||||||
|
#- uses: actions/download-artifact@v4
|
||||||
|
#with:
|
||||||
|
#name: release-i686-pc-windows-msvc
|
||||||
|
#path:
|
||||||
|
#build
|
||||||
|
#- uses: actions/download-artifact@v4
|
||||||
|
#with:
|
||||||
|
#name: release-aarch64-pc-windows-msvc
|
||||||
|
#path: rust/build
|
||||||
|
- run: ls -al ./rust/build
|
||||||
|
|
||||||
|
- name: Publish Release
|
||||||
|
uses: ./.github/actions/github-release
|
||||||
|
with:
|
||||||
|
files: "rust/build/*"
|
||||||
|
name: ${{ env.TAG }}
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: move files to dir for upload
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd rust
|
||||||
|
mkdir -p releases/language-server/${{ env.TAG }}
|
||||||
|
cp -r build/* releases/language-server/${{ env.TAG }}
|
||||||
|
|
||||||
|
- name: "Authenticate to Google Cloud"
|
||||||
|
uses: "google-github-actions/auth@v2.1.7"
|
||||||
|
with:
|
||||||
|
credentials_json: "${{ secrets.GOOGLE_CLOUD_DL_SA }}"
|
||||||
|
- name: Set up Cloud SDK
|
||||||
|
uses: google-github-actions/setup-gcloud@v2.1.2
|
||||||
|
with:
|
||||||
|
project_id: kittycadapi
|
||||||
|
- name: "upload files to gcp"
|
||||||
|
id: upload-files
|
||||||
|
uses: google-github-actions/upload-cloud-storage@v2.2.1
|
||||||
|
with:
|
||||||
|
path: rust/releases
|
||||||
|
destination: dl.kittycad.io
|
||||||
|
|
||||||
|
- run: rm rust/build/kcl-language-server-no-server.vsix
|
||||||
|
|
||||||
|
- name: Publish Extension (Code Marketplace, release)
|
||||||
|
# token from https://dev.azure.com/kcl-language-server/
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
npx vsce publish --pat ${{ secrets.VSCE_PAT }} --packagePath ../build/kcl-language-server-*.vsix
|
||||||
|
|
||||||
|
- name: Publish Extension (OpenVSX, release)
|
||||||
|
run: |
|
||||||
|
cd rust/kcl-language-server
|
||||||
|
npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../build/kcl-language-server-*.vsix
|
||||||
|
timeout-minutes: 2
|
4
.github/workflows/static-analysis.yml
vendored
4
.github/workflows/static-analysis.yml
vendored
@ -52,6 +52,7 @@ jobs:
|
|||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
|
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: './rust'
|
workspaces: './rust'
|
||||||
@ -72,6 +73,7 @@ jobs:
|
|||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
|
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
|
|
||||||
python-codespell:
|
python-codespell:
|
||||||
@ -141,7 +143,7 @@ jobs:
|
|||||||
|
|
||||||
- name: run unit tests
|
- name: run unit tests
|
||||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
run: yarn test:unit
|
run: xvfb-run -a yarn test:unit
|
||||||
env:
|
env:
|
||||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
|
|
||||||
|
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,7 +1,7 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
|
||||||
@ -9,7 +9,7 @@
|
|||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
build
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@ -35,6 +35,10 @@ rust/lcov.info
|
|||||||
rust/kcl-wasm-lib/pkg
|
rust/kcl-wasm-lib/pkg
|
||||||
*.snap.new
|
*.snap.new
|
||||||
rust/kcl-lib/fuzz/Cargo.lock
|
rust/kcl-lib/fuzz/Cargo.lock
|
||||||
|
rust/kcl-language-server-release/Cargo.lock
|
||||||
|
|
||||||
|
# kcl language server package
|
||||||
|
rust/kcl-language-server/dist/
|
||||||
|
|
||||||
e2e/playwright/playwright-secrets.env
|
e2e/playwright/playwright-secrets.env
|
||||||
e2e/playwright/temp1.png
|
e2e/playwright/temp1.png
|
||||||
@ -71,5 +75,7 @@ out/
|
|||||||
# python
|
# python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
uv.lock
|
uv.lock
|
||||||
rust/kcl-python-bindings/dist
|
dist
|
||||||
venv
|
venv
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
@ -7,10 +7,10 @@ coverage
|
|||||||
*.rs
|
*.rs
|
||||||
*.hbs
|
*.hbs
|
||||||
target
|
target
|
||||||
rust/
|
|
||||||
e2e/playwright/export-snapshots
|
e2e/playwright/export-snapshots
|
||||||
e2e/playwright/snapshots/prompt-to-edit
|
e2e/playwright/snapshots/prompt-to-edit
|
||||||
|
|
||||||
|
|
||||||
# XState generated files
|
# XState generated files
|
||||||
src/machines/**.typegen.ts
|
src/machines/**.typegen.ts
|
||||||
|
.vscode-test
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
@ -87,8 +87,8 @@
|
|||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver:stop": "kill-port 3000",
|
"simpleserver:stop": "kill-port 3000",
|
||||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages ./rust/kcl-language-server",
|
||||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages ./rust/kcl-language-server",
|
||||||
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh",
|
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh",
|
||||||
"fetch:wasm:windows": "./scripts/get-latest-wasm-bundle.ps1",
|
"fetch:wasm:windows": "./scripts/get-latest-wasm-bundle.ps1",
|
||||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/next/manifest.json",
|
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/next/manifest.json",
|
||||||
@ -98,8 +98,8 @@
|
|||||||
"build:wasm:windows": "yarn install:wasm-pack:cargo && yarn build:wasm:nocopy && ./scripts/copy-wasm.ps1 && yarn fmt",
|
"build:wasm:windows": "yarn install:wasm-pack:cargo && yarn build:wasm:nocopy && ./scripts/copy-wasm.ps1 && yarn fmt",
|
||||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||||
"wasm-prep": "rimraf rust/kcl-wasm-lib/pkg && mkdirp rust/kcl-wasm-lib/pkg && rimraf rust/kcl-lib/bindings",
|
"wasm-prep": "rimraf rust/kcl-wasm-lib/pkg && mkdirp rust/kcl-wasm-lib/pkg && rimraf rust/kcl-lib/bindings",
|
||||||
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||||
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||||
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
||||||
"files:set-notes": "./scripts/set-files-notes.sh",
|
"files:set-notes": "./scripts/set-files-notes.sh",
|
||||||
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
||||||
|
2
rust/.cargo/config.toml
Normal file
2
rust/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[alias]
|
||||||
|
kcl-language-server-release = "run --manifest-path ./kcl-language-server-release/Cargo.toml --"
|
471
rust/Cargo.lock
generated
471
rust/Cargo.lock
generated
@ -27,6 +27,17 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.8.11"
|
version = "0.8.11"
|
||||||
@ -361,6 +372,26 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
|
||||||
|
dependencies = [
|
||||||
|
"bzip2-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2-sys"
|
||||||
|
version = "0.1.13+1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cast"
|
name = "cast"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -373,6 +404,8 @@ version = "1.2.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
|
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -442,6 +475,16 @@ dependencies = [
|
|||||||
"half",
|
"half",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.31"
|
version = "4.5.31"
|
||||||
@ -523,6 +566,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -557,6 +606,21 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc"
|
||||||
|
version = "3.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
||||||
|
dependencies = [
|
||||||
|
"crc-catalog",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc-catalog"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -604,6 +668,15 @@ dependencies = [
|
|||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.6"
|
version = "0.8.6"
|
||||||
@ -713,6 +786,12 @@ version = "2.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
|
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deflate64"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
@ -794,6 +873,28 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-next"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"dirs-sys-next",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys-next"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -899,9 +1000,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.35"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
@ -1182,6 +1283,15 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.11"
|
version = "0.5.11"
|
||||||
@ -1564,6 +1674,15 @@ version = "1.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
|
checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "insta"
|
name = "insta"
|
||||||
version = "1.42.1"
|
version = "1.42.1"
|
||||||
@ -1643,6 +1762,15 @@ version = "1.0.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.77"
|
version = "0.3.77"
|
||||||
@ -1683,6 +1811,47 @@ dependencies = [
|
|||||||
"syn 2.0.96",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kcl-language-server"
|
||||||
|
version = "0.2.45"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"dashmap 6.1.0",
|
||||||
|
"kcl-lib",
|
||||||
|
"kittycad",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"signal-hook",
|
||||||
|
"slog",
|
||||||
|
"slog-async",
|
||||||
|
"slog-json",
|
||||||
|
"slog-term",
|
||||||
|
"tokio",
|
||||||
|
"tower-lsp",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kcl-language-server-release"
|
||||||
|
version = "0.1.45"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"flate2",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"slog",
|
||||||
|
"slog-async",
|
||||||
|
"slog-json",
|
||||||
|
"slog-term",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"xshell",
|
||||||
|
"zip",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.45"
|
version = "0.2.45"
|
||||||
@ -1930,6 +2099,16 @@ version = "0.2.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.8.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
@ -1959,10 +2138,16 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "lockfree-object-pool"
|
||||||
version = "0.4.25"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -1989,6 +2174,16 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lzma-rs"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"crc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "measurements"
|
name = "measurements"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -2068,9 +2263,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.3"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
@ -2118,6 +2313,16 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@ -2209,6 +2414,12 @@ version = "0.1.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owo-colors"
|
name = "owo-colors"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
@ -2299,6 +2510,16 @@ dependencies = [
|
|||||||
"syn 2.0.96",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"hmac",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@ -2408,6 +2629,12 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotters"
|
name = "plotters"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
@ -2747,6 +2974,17 @@ dependencies = [
|
|||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.15",
|
||||||
|
"libredox",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.11.1"
|
version = "1.11.1"
|
||||||
@ -3197,12 +3435,31 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -3233,6 +3490,49 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slog"
|
||||||
|
version = "2.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slog-async"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72c8038f898a2c79507940990f05386455b3a317d8f18d4caea7cbc3d5096b84"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"slog",
|
||||||
|
"take_mut",
|
||||||
|
"thread_local",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slog-json"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e1e53f61af1e3c8b852eef0a9dee29008f55d6dd63794f3f12cef786cf0f219"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"slog",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slog-term"
|
||||||
|
version = "2.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8"
|
||||||
|
dependencies = [
|
||||||
|
"is-terminal",
|
||||||
|
"slog",
|
||||||
|
"term",
|
||||||
|
"thread_local",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
@ -3410,6 +3710,12 @@ dependencies = [
|
|||||||
"syn 2.0.96",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "take_mut"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tap"
|
name = "tap"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -3436,6 +3742,17 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "term"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-next",
|
||||||
|
"rustversion",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
@ -3511,6 +3828,16 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.37"
|
version = "0.3.37"
|
||||||
@ -3798,6 +4125,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-serde"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||||
|
dependencies = [
|
||||||
|
"nu-ansi-term",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
"tracing-serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4004,6 +4370,12 @@ dependencies = [
|
|||||||
"syn 2.0.96",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@ -4349,6 +4721,21 @@ dependencies = [
|
|||||||
"tap",
|
"tap",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xshell"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d"
|
||||||
|
dependencies = [
|
||||||
|
"xshell-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xshell-macros"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
@ -4435,6 +4822,20 @@ name = "zeroize"
|
|||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.96",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec"
|
name = "zerovec"
|
||||||
@ -4464,11 +4865,67 @@ version = "2.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45"
|
checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aes",
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
|
"bzip2",
|
||||||
|
"constant_time_eq",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
|
"deflate64",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
|
"flate2",
|
||||||
|
"hmac",
|
||||||
"indexmap 2.7.1",
|
"indexmap 2.7.1",
|
||||||
|
"lzma-rs",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
"pbkdf2",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"sha1",
|
||||||
"thiserror 2.0.11",
|
"thiserror 2.0.11",
|
||||||
|
"time",
|
||||||
|
"zeroize",
|
||||||
|
"zopfli",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zopfli"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"crc32fast",
|
||||||
|
"lockfree-object-pool",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "7.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "2.0.14+zstd.1.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
@ -3,6 +3,8 @@ resolver = "2"
|
|||||||
members = [
|
members = [
|
||||||
"kcl-bumper",
|
"kcl-bumper",
|
||||||
"kcl-derive-docs",
|
"kcl-derive-docs",
|
||||||
|
"kcl-language-server",
|
||||||
|
"kcl-language-server-release",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
"kcl-python-bindings",
|
"kcl-python-bindings",
|
||||||
"kcl-test-server",
|
"kcl-test-server",
|
||||||
@ -27,17 +29,26 @@ debug = "line-tables-only"
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
async-trait = "0.1.85"
|
async-trait = "0.1.85"
|
||||||
anyhow = { version = "1" }
|
anyhow = { version = "1" }
|
||||||
|
clap = { version = "4.5.31", features = ["derive"] }
|
||||||
|
dashmap = { version = "6.1.0" }
|
||||||
http = "1"
|
http = "1"
|
||||||
indexmap = "2.7.0"
|
indexmap = "2.7.0"
|
||||||
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
|
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
|
||||||
kittycad-modeling-cmds = { version = "0.2.100", features = ["ts-rs", "websocket"] }
|
kittycad-modeling-cmds = { version = "0.2.100", features = ["ts-rs", "websocket"] }
|
||||||
|
lazy_static = "1.5.0"
|
||||||
miette = "7.5.0"
|
miette = "7.5.0"
|
||||||
pyo3 = { version = "0.22.6" }
|
pyo3 = { version = "0.22.6" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = { version = "1" }
|
serde_json = { version = "1" }
|
||||||
|
slog = "2.7.0"
|
||||||
|
slog-async = "2.8.0"
|
||||||
|
slog-json = "2.6.1"
|
||||||
|
slog-term = "2.9.1"
|
||||||
tokio = { version = "1" }
|
tokio = { version = "1" }
|
||||||
tower-lsp = { version = "0.20.0", default-features = false }
|
tower-lsp = { version = "0.20.0", default-features = false }
|
||||||
|
tracing-subscriber = { version = "0.3.19", features = ["registry", "std", "fmt", "smallvec", "ansi", "tracing-log", "json"] }
|
||||||
uuid = { version = "1", features = ["v4", "serde"] }
|
uuid = { version = "1", features = ["v4", "serde"] }
|
||||||
|
zip = { version = "2.2.2", default-features = false }
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
assertions_on_result_states = "warn"
|
assertions_on_result_states = "warn"
|
||||||
|
@ -20,4 +20,5 @@
|
|||||||
- This will publish the relevant crates and push a new tag with the prefix
|
- This will publish the relevant crates and push a new tag with the prefix
|
||||||
`kcl-`. DO NOT SET THE PREFIX TO `kcl-` when you run the command. The `just`
|
`kcl-`. DO NOT SET THE PREFIX TO `kcl-` when you run the command. The `just`
|
||||||
command will do that for you.
|
command will do that for you.
|
||||||
- The tag will then trigger the release of kcl-python-bindings.
|
- The tag will then trigger the release of `kcl-python-bindings` and
|
||||||
|
`kcl-language-server`.
|
||||||
|
@ -11,7 +11,7 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { version = "4.5.31", features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
semver = "1.0.25"
|
semver = "1.0.25"
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
toml_edit = "0.22.16"
|
toml_edit = "0.22.16"
|
||||||
|
26
rust/kcl-language-server-release/Cargo.toml
Normal file
26
rust/kcl-language-server-release/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "kcl-language-server-release"
|
||||||
|
version = "0.1.45"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
|
publish = false
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
clap = { workspace = true, features = ["cargo", "derive", "env", "unicode"] }
|
||||||
|
flate2 = "1.1.0"
|
||||||
|
lazy_static = { workspace = true }
|
||||||
|
log = { version = "0.4.26", features = ["serde"] }
|
||||||
|
slog = { workspace = true }
|
||||||
|
slog-async = { workspace = true }
|
||||||
|
slog-json = { workspace = true }
|
||||||
|
slog-term = { workspace = true }
|
||||||
|
time = "0.3.37"
|
||||||
|
tokio = { workspace = true, features = ["full"] }
|
||||||
|
tracing-subscriber = { workspace = true }
|
||||||
|
xshell = "0.2.6"
|
||||||
|
zip = { workspace = true, features = ["default"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
247
rust/kcl-language-server-release/src/build.rs
Normal file
247
rust/kcl-language-server-release/src/build.rs
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
fs::File,
|
||||||
|
io::{self, BufWriter},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use flate2::{write::GzEncoder, Compression};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
use xshell::{cmd, Shell};
|
||||||
|
use zip::ZipWriter;
|
||||||
|
|
||||||
|
/// A subcommand for building and packaging a release.
|
||||||
|
#[derive(Parser, Clone, Debug)]
|
||||||
|
pub struct Build {
|
||||||
|
/// An optional client patch version to use.
|
||||||
|
#[clap(long = "client-patch-version", default_value = "None")]
|
||||||
|
pub client_patch_version: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Build {
|
||||||
|
pub(crate) fn run(&self, sh: &Shell) -> Result<()> {
|
||||||
|
let stable = sh
|
||||||
|
.var("GITHUB_REF")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_str()
|
||||||
|
.contains("refs/tags/v");
|
||||||
|
|
||||||
|
let project_root = crate::project_root();
|
||||||
|
let target = Target::get(&project_root);
|
||||||
|
let build = project_root.join("build");
|
||||||
|
sh.remove_path(&build)?;
|
||||||
|
sh.create_dir(&build)?;
|
||||||
|
|
||||||
|
// Read the version from our root Cargo.toml.
|
||||||
|
let version = sh.read_file("kcl-language-server/Cargo.toml")?;
|
||||||
|
let mut version = version
|
||||||
|
.lines()
|
||||||
|
.find(|line| line.starts_with("version = "))
|
||||||
|
.unwrap_or_default()
|
||||||
|
.replace("version = ", "")
|
||||||
|
.replace(['\"', '\''], "")
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if !stable {
|
||||||
|
version = format!("{}-nightly", version);
|
||||||
|
}
|
||||||
|
|
||||||
|
let release_tag = if stable {
|
||||||
|
// We already checked above if the env var contains "refs/tags/v".
|
||||||
|
// So this is safe to unwrap.
|
||||||
|
sh.var("GITHUB_REF")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.replace("refs/tags/", "")
|
||||||
|
.to_string()
|
||||||
|
} else {
|
||||||
|
"nightly".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
if stable && !release_tag.contains(&version) {
|
||||||
|
// bail early if the tag doesn't match the version
|
||||||
|
anyhow::bail!(
|
||||||
|
"Tag {} doesn't match version {}. Did you forget to update Cargo.toml?",
|
||||||
|
release_tag,
|
||||||
|
version
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
build_server(sh, &version, &target)?;
|
||||||
|
build_client(sh, &version, &release_tag, &target)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_client(sh: &Shell, version: &str, release_tag: &str, target: &Target) -> anyhow::Result<()> {
|
||||||
|
let bundle_path = Path::new("server");
|
||||||
|
sh.create_dir(bundle_path)?;
|
||||||
|
sh.copy_file(&target.server_path, bundle_path)?;
|
||||||
|
if let Some(symbols_path) = &target.symbols_path {
|
||||||
|
sh.copy_file(symbols_path, bundle_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut patch = Patch::new(sh, "./kcl-language-server/package.json")?;
|
||||||
|
patch
|
||||||
|
.replace(r#""version": "0.0.0""#, &format!(r#""version": "{version}""#))
|
||||||
|
.replace(r#""releaseTag": null"#, &format!(r#""releaseTag": "{release_tag}""#))
|
||||||
|
.replace(r#""enabledApiProposals": [],"#, r#""#);
|
||||||
|
patch.commit(sh)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_server(sh: &Shell, release: &str, target: &Target) -> anyhow::Result<()> {
|
||||||
|
let _e = sh.push_env("CFG_RELEASE", release);
|
||||||
|
let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin");
|
||||||
|
|
||||||
|
// Uncomment to enable debug info for releases. Note that:
|
||||||
|
// * debug info is split on windows and macs, so it does nothing for those platforms,
|
||||||
|
// * on Linux, this blows up the binary size from 8MB to 43MB, which is unreasonable.
|
||||||
|
// let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "1");
|
||||||
|
|
||||||
|
if target.name.contains("-linux-") {
|
||||||
|
env::set_var("CC", "clang");
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_name = &target.name;
|
||||||
|
cmd!(
|
||||||
|
sh,
|
||||||
|
"cargo build -p kcl-language-server --target {target_name} --release"
|
||||||
|
)
|
||||||
|
.run()?;
|
||||||
|
|
||||||
|
let dst = Path::new("build").join(&target.artifact_name);
|
||||||
|
gzip(&target.server_path, &dst.with_extension("gz"))?;
|
||||||
|
if target_name.contains("-windows-") {
|
||||||
|
zip(
|
||||||
|
&target.server_path,
|
||||||
|
target.symbols_path.as_ref(),
|
||||||
|
&dst.with_extension("zip"),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gzip(src_path: &Path, dest_path: &Path) -> anyhow::Result<()> {
|
||||||
|
let mut encoder = GzEncoder::new(File::create(dest_path)?, Compression::best());
|
||||||
|
let mut input = io::BufReader::new(File::open(src_path)?);
|
||||||
|
io::copy(&mut input, &mut encoder)?;
|
||||||
|
encoder.finish()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zip(src_path: &Path, symbols_path: Option<&PathBuf>, dest_path: &Path) -> anyhow::Result<()> {
|
||||||
|
let file = File::create(dest_path)?;
|
||||||
|
let mut writer = ZipWriter::new(BufWriter::new(file));
|
||||||
|
let file_options = zip::write::SimpleFileOptions::default()
|
||||||
|
.last_modified_time(convert_date_time(OffsetDateTime::from(
|
||||||
|
std::fs::metadata(src_path)?.modified()?,
|
||||||
|
))?)
|
||||||
|
.unix_permissions(0o755)
|
||||||
|
.compression_method(zip::CompressionMethod::Deflated)
|
||||||
|
.compression_level(Some(9));
|
||||||
|
writer.start_file(src_path.file_name().unwrap().to_str().unwrap(), file_options)?;
|
||||||
|
let mut input = io::BufReader::new(File::open(src_path)?);
|
||||||
|
io::copy(&mut input, &mut writer)?;
|
||||||
|
if let Some(symbols_path) = symbols_path {
|
||||||
|
writer.start_file(symbols_path.file_name().unwrap().to_str().unwrap(), file_options)?;
|
||||||
|
let mut input = io::BufReader::new(File::open(symbols_path)?);
|
||||||
|
io::copy(&mut input, &mut writer)?;
|
||||||
|
}
|
||||||
|
writer.finish()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Target {
|
||||||
|
name: String,
|
||||||
|
server_path: PathBuf,
|
||||||
|
symbols_path: Option<PathBuf>,
|
||||||
|
artifact_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Target {
|
||||||
|
fn get(project_root: &Path) -> Self {
|
||||||
|
let name = match env::var("RA_TARGET") {
|
||||||
|
Ok(target) => target,
|
||||||
|
_ => {
|
||||||
|
if cfg!(target_os = "linux") {
|
||||||
|
"x86_64-unknown-linux-gnu".to_string()
|
||||||
|
} else if cfg!(target_os = "windows") {
|
||||||
|
"x86_64-pc-windows-msvc".to_string()
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
"aarch64-apple-darwin".to_string()
|
||||||
|
} else {
|
||||||
|
panic!("Unsupported OS, maybe try setting RA_TARGET")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let out_path = project_root.join("target").join(&name).join("release");
|
||||||
|
let (exe_suffix, symbols_path) = if name.contains("-windows-") {
|
||||||
|
(".exe".into(), Some(out_path.join("kcl_language_server.pdb")))
|
||||||
|
} else {
|
||||||
|
(String::new(), None)
|
||||||
|
};
|
||||||
|
let server_path = out_path.join(format!("kcl-language-server{exe_suffix}"));
|
||||||
|
let artifact_name = format!("kcl-language-server-{name}{exe_suffix}");
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
server_path,
|
||||||
|
symbols_path,
|
||||||
|
artifact_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Patch {
|
||||||
|
path: PathBuf,
|
||||||
|
original_contents: String,
|
||||||
|
contents: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Patch {
|
||||||
|
fn new(sh: &Shell, path: impl Into<PathBuf>) -> anyhow::Result<Patch> {
|
||||||
|
let path = path.into();
|
||||||
|
let contents = sh.read_file(&path)?;
|
||||||
|
Ok(Patch {
|
||||||
|
path,
|
||||||
|
original_contents: contents.clone(),
|
||||||
|
contents,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace(&mut self, from: &str, to: &str) -> &mut Patch {
|
||||||
|
assert!(self.contents.contains(from));
|
||||||
|
self.contents = self.contents.replace(from, to);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(&self, sh: &Shell) -> anyhow::Result<()> {
|
||||||
|
sh.write_file(&self.path, &self.contents)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Patch {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// FIXME: find a way to bring this back
|
||||||
|
let _ = &self.original_contents;
|
||||||
|
// write_file(&self.path, &self.original_contents).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_date_time(offset_dt: OffsetDateTime) -> anyhow::Result<zip::DateTime> {
|
||||||
|
// Convert to MS-DOS date time format that the zip crate expects
|
||||||
|
zip::DateTime::from_date_and_time(
|
||||||
|
offset_dt.year() as u16,
|
||||||
|
offset_dt.month() as u8,
|
||||||
|
offset_dt.day(),
|
||||||
|
offset_dt.hour(),
|
||||||
|
offset_dt.minute(),
|
||||||
|
offset_dt.second(),
|
||||||
|
)
|
||||||
|
.map_err(|err| anyhow::anyhow!("Failed to convert date time to MS-DOS format: {}", err))
|
||||||
|
}
|
157
rust/kcl-language-server-release/src/main.rs
Normal file
157
rust/kcl-language-server-release/src/main.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
//! A release building tool and packager.
|
||||||
|
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
/// A subcommand for building and packaging a release.
|
||||||
|
pub mod build;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use clap::Parser;
|
||||||
|
use slog::Drain;
|
||||||
|
use tracing_subscriber::{prelude::*, Layer};
|
||||||
|
use xshell::Shell;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
/// Initialize the logger.
|
||||||
|
// We need a slog::Logger for steno and when we export out the logs from re-exec-ed processes.
|
||||||
|
pub static ref LOGGER: slog::Logger = {
|
||||||
|
let decorator = slog_term::TermDecorator::new().build();
|
||||||
|
let drain = slog_term::FullFormat::new(decorator).build().fuse();
|
||||||
|
let drain = slog_async::Async::new(drain).build().fuse();
|
||||||
|
slog::Logger::root(drain, slog::slog_o!())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This doc string acts as a help message when the user runs '--help'
|
||||||
|
/// as do all doc strings on fields.
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
#[clap(version = clap::crate_version!(), author = clap::crate_authors!("\n"))]
|
||||||
|
pub struct Opts {
|
||||||
|
/// Print debug info
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub debug: bool,
|
||||||
|
|
||||||
|
/// Print logs as json
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub json: bool,
|
||||||
|
|
||||||
|
/// The subcommand to run.
|
||||||
|
#[clap(subcommand)]
|
||||||
|
pub subcmd: SubCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Opts {
|
||||||
|
/// Setup our logger.
|
||||||
|
pub fn create_logger(&self) -> slog::Logger {
|
||||||
|
if self.json {
|
||||||
|
let drain = slog_json::Json::default(std::io::stderr()).fuse();
|
||||||
|
self.async_root_logger(drain)
|
||||||
|
} else {
|
||||||
|
let decorator = slog_term::TermDecorator::new().build();
|
||||||
|
let drain = slog_term::FullFormat::new(decorator).build().fuse();
|
||||||
|
self.async_root_logger(drain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn async_root_logger<T>(&self, drain: T) -> slog::Logger
|
||||||
|
where
|
||||||
|
T: slog::Drain + Send + 'static,
|
||||||
|
<T as slog::Drain>::Err: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
let level = if self.debug {
|
||||||
|
slog::Level::Debug
|
||||||
|
} else {
|
||||||
|
slog::Level::Info
|
||||||
|
};
|
||||||
|
|
||||||
|
let level_drain = slog::LevelFilter(drain, level).fuse();
|
||||||
|
let async_drain = slog_async::Async::new(level_drain).build().fuse();
|
||||||
|
slog::Logger::root(async_drain, slog::o!())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A subcommand for our cli.
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
pub enum SubCommand {
|
||||||
|
/// Build release packages.
|
||||||
|
Build(crate::build::Build),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
let opts: Opts = Opts::parse();
|
||||||
|
|
||||||
|
let level_filter = if opts.debug {
|
||||||
|
tracing_subscriber::filter::LevelFilter::DEBUG
|
||||||
|
} else {
|
||||||
|
tracing_subscriber::filter::LevelFilter::INFO
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format fields using the provided closure.
|
||||||
|
// We want to make this very consise otherwise the logs are not able to be read by humans.
|
||||||
|
let format = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| {
|
||||||
|
if format!("{}", field) == "message" {
|
||||||
|
write!(writer, "{}: {:?}", field, value)
|
||||||
|
} else {
|
||||||
|
write!(writer, "{}", field)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Separate each field with a comma.
|
||||||
|
// This method is provided by an extension trait in the
|
||||||
|
// `tracing-subscriber` prelude.
|
||||||
|
.delimited(", ");
|
||||||
|
|
||||||
|
let (json, plain) = if opts.json {
|
||||||
|
// Cloud run likes json formatted logs if possible.
|
||||||
|
// See: https://cloud.google.com/run/docs/logging
|
||||||
|
// We could probably format these specifically for cloud run if we wanted,
|
||||||
|
// will save that as a TODO: https://cloud.google.com/run/docs/logging#special-fields
|
||||||
|
(
|
||||||
|
Some(tracing_subscriber::fmt::layer().json().with_filter(level_filter)),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
Some(
|
||||||
|
tracing_subscriber::fmt::layer()
|
||||||
|
.pretty()
|
||||||
|
.fmt_fields(format)
|
||||||
|
.with_filter(level_filter),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize the tracing.
|
||||||
|
tracing_subscriber::registry().with(json).with(plain).init();
|
||||||
|
|
||||||
|
if let Err(err) = run_cmd(&opts).await {
|
||||||
|
bail!("running cmd `{:?}` failed: {:?}", &opts.subcmd, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_cmd(opts: &Opts) -> Result<()> {
|
||||||
|
let sh = &Shell::new()?;
|
||||||
|
sh.change_dir(project_root());
|
||||||
|
|
||||||
|
match &opts.subcmd {
|
||||||
|
SubCommand::Build(b) => b.run(sh)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn project_root() -> PathBuf {
|
||||||
|
Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()))
|
||||||
|
.ancestors()
|
||||||
|
.nth(1)
|
||||||
|
.unwrap()
|
||||||
|
.to_path_buf()
|
||||||
|
}
|
53
rust/kcl-language-server/Cargo.toml
Normal file
53
rust/kcl-language-server/Cargo.toml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
[package]
|
||||||
|
name = "kcl-language-server"
|
||||||
|
description = "A language server for KCL."
|
||||||
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
|
version = "0.2.45"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "kcl-language-server"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
clap = { workspace = true, features = ["cargo", "derive", "env", "unicode"] }
|
||||||
|
dashmap = { workspace = true }
|
||||||
|
kcl-lib = { path = "../kcl-lib", default-features = false, features = [
|
||||||
|
"cli",
|
||||||
|
"engine",
|
||||||
|
"disable-println",
|
||||||
|
] }
|
||||||
|
kittycad = { workspace = true }
|
||||||
|
lazy_static = { workspace = true }
|
||||||
|
log = { version = "0.4.26", features = ["serde"] }
|
||||||
|
slog = { workspace = true }
|
||||||
|
slog-async = { workspace = true }
|
||||||
|
slog-json = { workspace = true }
|
||||||
|
slog-term = { workspace = true }
|
||||||
|
tracing-subscriber = { workspace = true }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
signal-hook = "0.3.17"
|
||||||
|
tokio = { version = "1.43.0", features = ["full"] }
|
||||||
|
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
tower-lsp = { version = "0.20.0", default-features = false, features = [
|
||||||
|
"runtime-agnostic",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
# Disabling debug info speeds up builds a bunch,
|
||||||
|
# and we don't rely on it for debugging that much.
|
||||||
|
debug = 0
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
incremental = true
|
||||||
|
# Set this to 1 or 2 to get more useful backtraces in debugger.
|
||||||
|
debug = 0
|
101
rust/kcl-language-server/README.md
Normal file
101
rust/kcl-language-server/README.md
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# kcl-lsp
|
||||||
|
|
||||||
|
The `kcl` [Language Server Protocol](https://microsoft.github.io/language-server-protocol)
|
||||||
|
implementation and VSCode extension.
|
||||||
|
|
||||||
|
This language server is a thin wrapper around the KCL language tooling library.
|
||||||
|
That is found in the [modeling-app](https://github.com/kittycad/modeling-app) repo, and published as
|
||||||
|
on crates.io as [kcl-lib](https://crates.io/crates/kcl-lib).
|
||||||
|
|
||||||
|
## VSCode
|
||||||
|
|
||||||
|
Install our extension: [KittyCAD Language Server](https://marketplace.visualstudio.com/items?itemName=KittyCAD.kcl-language-server)
|
||||||
|
|
||||||
|
## Neovim
|
||||||
|
|
||||||
|
You can add the following to your `vim` configuration if you are using `lspconfig`.
|
||||||
|
|
||||||
|
This is [@jessfraz's
|
||||||
|
setup](https://github.com/jessfraz/.vim/blob/master/vimrc#L935).
|
||||||
|
|
||||||
|
```vim
|
||||||
|
if executable('kcl-language-server')
|
||||||
|
lua << EOF
|
||||||
|
local lspconfig = require 'lspconfig'
|
||||||
|
local configs = require 'lspconfig.configs'
|
||||||
|
|
||||||
|
if not configs.kcl_lsp then
|
||||||
|
configs.kcl_lsp = {
|
||||||
|
default_config = {
|
||||||
|
cmd = {'kcl-language-server', 'server', '--stdio'},
|
||||||
|
filetypes = {'kcl'},
|
||||||
|
root_dir = lspconfig.util.root_pattern('.git'),
|
||||||
|
single_file_support = true,
|
||||||
|
},
|
||||||
|
docs = {
|
||||||
|
description = [=[
|
||||||
|
https://github.com/KittyCAD/kcl-lsp
|
||||||
|
https://kittycad.io
|
||||||
|
|
||||||
|
The KittyCAD Language Server Protocol implementation for the KCL language.
|
||||||
|
|
||||||
|
To better detect kcl files, the following can be added:
|
||||||
|
|
||||||
|
|
||||||
|
vim.cmd [[ autocmd BufRead,BufNewFile *.kcl set filetype=kcl ]]
|
||||||
|
|
||||||
|
]=],
|
||||||
|
default_config = {
|
||||||
|
root_dir = [[root_pattern(".git")]],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
lspconfig.kcl_lsp.setup{}
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
echo "You might want to install kcl-language-server: https://github.com/KittyCAD/kcl-lsp/releases"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helix
|
||||||
|
|
||||||
|
Add this to your `languages.toml` file. Remember to change `/Users/adamchalmers` to your path.
|
||||||
|
|
||||||
|
Note that we don't currently have Treesitter parsers, so there won't be syntax highlighting.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[language]]
|
||||||
|
name = "kcl"
|
||||||
|
scope = "source.kcl"
|
||||||
|
injection-regex = "kcl"
|
||||||
|
file-types = ["kcl"]
|
||||||
|
comment-tokens = "//"
|
||||||
|
indent = { tab-width = 2, unit = " " }
|
||||||
|
language-servers = [ "kcl-lsp" ]
|
||||||
|
block-comment-tokens = { start = "/*", end = "*/"}
|
||||||
|
|
||||||
|
|
||||||
|
[language-server.kcl-lsp]
|
||||||
|
command = "/Users/adamchalmers/kc-repos/kcl-lsp/target/release/kcl-language-server"
|
||||||
|
args = ["server", "--stdio"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ yarn install
|
||||||
|
$ cargo build
|
||||||
|
$ code .
|
||||||
|
```
|
||||||
|
|
||||||
|
Once VSCode opens, go to the "Run and Debug" panel (cmd-shift-D on MacOS), and choose Run Extension (Debug Build).
|
||||||
|
This opens a new VSCode window with our KCL extension installed. Open a KCL file and check that the LSP is working.
|
||||||
|
|
||||||
|
- press <kbd>F5</kbd> or change to the Debug panel and click <kbd>Launch Client</kbd>
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> If encountered errors like `Cannot find module '/xxx/xxx/dist/extension.js'`
|
||||||
|
> please try run command `tsc -b` manually
|
162
rust/kcl-language-server/client/src/bootstrap.ts
Normal file
162
rust/kcl-language-server/client/src/bootstrap.ts
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import * as vscode from 'vscode'
|
||||||
|
import * as os from 'os'
|
||||||
|
import type { Config } from './config'
|
||||||
|
import { log, isValidExecutable } from './util'
|
||||||
|
import type { PersistentState } from './persistent_state'
|
||||||
|
import { exec } from 'child_process'
|
||||||
|
|
||||||
|
export async function bootstrap(
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
config: Config,
|
||||||
|
state: PersistentState
|
||||||
|
): Promise<string> {
|
||||||
|
const path = await getServer(context, config, state)
|
||||||
|
if (!path) {
|
||||||
|
throw new Error(
|
||||||
|
'KittyCAD Language Server is not available. ' +
|
||||||
|
'Please, ensure its [proper installation](https://github.com/kittycad/kcl-lsp).'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info('Using server binary at', path)
|
||||||
|
|
||||||
|
if (!isValidExecutable(path)) {
|
||||||
|
if (config.serverPath) {
|
||||||
|
throw new Error(`Failed to execute ${path} --version. \`config.server.path\` or \`config.serverPath\` has been set explicitly.\
|
||||||
|
Consider removing this config or making a valid server binary available at that path.`)
|
||||||
|
} else {
|
||||||
|
throw new Error(`Failed to execute ${path} --version`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
async function getServer(
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
config: Config,
|
||||||
|
state: PersistentState
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
const explicitPath =
|
||||||
|
process.env['__KCL_LSP_SERVER_DEBUG'] ?? config.serverPath
|
||||||
|
if (explicitPath) {
|
||||||
|
if (explicitPath.startsWith('~/')) {
|
||||||
|
return os.homedir() + explicitPath.slice('~'.length)
|
||||||
|
}
|
||||||
|
return explicitPath
|
||||||
|
}
|
||||||
|
if (config.package.releaseTag === null) return 'kcl-language-server'
|
||||||
|
|
||||||
|
const ext = process.platform === 'win32' ? '.exe' : ''
|
||||||
|
const bundled = vscode.Uri.joinPath(
|
||||||
|
context.extensionUri,
|
||||||
|
'server',
|
||||||
|
`kcl-language-server${ext}`
|
||||||
|
)
|
||||||
|
log.info('Checking if bundled server exists at', bundled)
|
||||||
|
const bundledExists = await vscode.workspace.fs.stat(bundled).then(
|
||||||
|
() => true,
|
||||||
|
() => false
|
||||||
|
)
|
||||||
|
log.info('Bundled server exists:', bundledExists)
|
||||||
|
if (bundledExists) {
|
||||||
|
let server = bundled
|
||||||
|
if (await isNixOs()) {
|
||||||
|
await vscode.workspace.fs.createDirectory(config.globalStorageUri).then()
|
||||||
|
const dest = vscode.Uri.joinPath(
|
||||||
|
config.globalStorageUri,
|
||||||
|
`kcl-language-server${ext}`
|
||||||
|
)
|
||||||
|
let exists = await vscode.workspace.fs.stat(dest).then(
|
||||||
|
() => true,
|
||||||
|
() => false
|
||||||
|
)
|
||||||
|
if (exists && config.package.version !== state.serverVersion) {
|
||||||
|
log.info(
|
||||||
|
'Server version changed, removing old server binary',
|
||||||
|
config.package.version,
|
||||||
|
state.serverVersion
|
||||||
|
)
|
||||||
|
await vscode.workspace.fs.delete(dest)
|
||||||
|
exists = false
|
||||||
|
}
|
||||||
|
if (!exists) {
|
||||||
|
await vscode.workspace.fs.copy(bundled, dest)
|
||||||
|
await patchelf(dest)
|
||||||
|
}
|
||||||
|
server = dest
|
||||||
|
}
|
||||||
|
|
||||||
|
await state.updateServerVersion(config.package.version)
|
||||||
|
return server.fsPath
|
||||||
|
}
|
||||||
|
|
||||||
|
await state.updateServerVersion(undefined)
|
||||||
|
await vscode.window.showErrorMessage(
|
||||||
|
"Unfortunately we don't ship binaries for your platform yet. " +
|
||||||
|
'You need to manually clone the kcl-lsp repository and ' +
|
||||||
|
'run `cargo install` to build the language server from sources. ' +
|
||||||
|
'If you feel that your platform should be supported, please create an issue ' +
|
||||||
|
'about that [here](https://github.com/kittycad/kcl-lsp/issues) and we ' +
|
||||||
|
'will consider it.'
|
||||||
|
)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isNixOs(): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const contents = (
|
||||||
|
await vscode.workspace.fs.readFile(vscode.Uri.file('/etc/os-release'))
|
||||||
|
).toString()
|
||||||
|
const idString =
|
||||||
|
contents.split('\n').find((a) => a.startsWith('ID=')) || 'ID=linux'
|
||||||
|
return idString.indexOf('nixos') !== -1
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function patchelf(dest: vscode.Uri): Promise<void> {
|
||||||
|
await vscode.window.withProgress(
|
||||||
|
{
|
||||||
|
location: vscode.ProgressLocation.Notification,
|
||||||
|
title: 'Patching kcl-language-server for NixOS',
|
||||||
|
},
|
||||||
|
async (progress, _) => {
|
||||||
|
const expression = `
|
||||||
|
{srcStr, pkgs ? import <nixpkgs> {}}:
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "kcl-language-server";
|
||||||
|
src = /. + srcStr;
|
||||||
|
phases = [ "installPhase" "fixupPhase" ];
|
||||||
|
installPhase = "cp $src $out";
|
||||||
|
fixupPhase = ''
|
||||||
|
chmod 755 $out
|
||||||
|
patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const origFile = vscode.Uri.file(dest.fsPath + '-orig')
|
||||||
|
await vscode.workspace.fs.rename(dest, origFile, { overwrite: true })
|
||||||
|
try {
|
||||||
|
progress.report({ message: 'Patching executable', increment: 20 })
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const handle = exec(
|
||||||
|
`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
|
||||||
|
(err, stdout, stderr) => {
|
||||||
|
if (err != null) {
|
||||||
|
reject(Error(stderr))
|
||||||
|
} else {
|
||||||
|
resolve(stdout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
handle.stdin?.write(expression)
|
||||||
|
handle.stdin?.end()
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
await vscode.workspace.fs.delete(origFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
74
rust/kcl-language-server/client/src/client.ts
Normal file
74
rust/kcl-language-server/client/src/client.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import * as lc from 'vscode-languageclient/node'
|
||||||
|
import type * as vscode from 'vscode'
|
||||||
|
|
||||||
|
export async function createClient(
|
||||||
|
traceOutputChannel: vscode.OutputChannel,
|
||||||
|
outputChannel: vscode.OutputChannel,
|
||||||
|
initializationOptions: vscode.WorkspaceConfiguration,
|
||||||
|
serverOptions: lc.ServerOptions
|
||||||
|
): Promise<lc.LanguageClient> {
|
||||||
|
const clientOptions: lc.LanguageClientOptions = {
|
||||||
|
documentSelector: [{ scheme: 'file', language: 'kcl' }],
|
||||||
|
initializationOptions,
|
||||||
|
traceOutputChannel,
|
||||||
|
outputChannel,
|
||||||
|
middleware: {
|
||||||
|
workspace: {
|
||||||
|
// HACK: This is a workaround, when the client has been disposed, VSCode
|
||||||
|
// continues to emit events to the client and the default one for this event
|
||||||
|
// attempt to restart the client for no reason
|
||||||
|
async didChangeWatchedFile(event: any, next: any) {
|
||||||
|
if (client.isRunning()) {
|
||||||
|
await next(event)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async configuration(
|
||||||
|
params: lc.ConfigurationParams,
|
||||||
|
token: vscode.CancellationToken,
|
||||||
|
next: lc.ConfigurationRequest.HandlerSignature
|
||||||
|
) {
|
||||||
|
const resp = await next(params, token)
|
||||||
|
return resp
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = new lc.LanguageClient(
|
||||||
|
'kcl-language-server',
|
||||||
|
'KittyCAD Language Server',
|
||||||
|
serverOptions,
|
||||||
|
clientOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
client.registerFeature(new ExperimentalFeatures())
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExperimentalFeatures implements lc.StaticFeature {
|
||||||
|
getState(): lc.FeatureState {
|
||||||
|
return { kind: 'static' }
|
||||||
|
}
|
||||||
|
fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
|
||||||
|
capabilities.experimental = {
|
||||||
|
snippetTextEdit: true,
|
||||||
|
codeActionGroup: true,
|
||||||
|
hoverActions: true,
|
||||||
|
serverStatusNotification: true,
|
||||||
|
colorDiagnosticOutput: true,
|
||||||
|
openServerLogs: true,
|
||||||
|
commands: {
|
||||||
|
commands: ['editor.action.triggerParameterHints'],
|
||||||
|
},
|
||||||
|
...capabilities.experimental,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initialize(
|
||||||
|
_capabilities: lc.ServerCapabilities,
|
||||||
|
_documentSelector: lc.DocumentSelector | undefined
|
||||||
|
): void {}
|
||||||
|
dispose(): void {}
|
||||||
|
clear(): void {}
|
||||||
|
}
|
32
rust/kcl-language-server/client/src/commands.ts
Normal file
32
rust/kcl-language-server/client/src/commands.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import * as vscode from 'vscode'
|
||||||
|
|
||||||
|
import type { Cmd, CtxInit } from './ctx'
|
||||||
|
import { spawnSync } from 'child_process'
|
||||||
|
|
||||||
|
export function serverVersion(ctx: CtxInit): Cmd {
|
||||||
|
return async () => {
|
||||||
|
if (!ctx.serverPath) {
|
||||||
|
void vscode.window.showWarningMessage(
|
||||||
|
`kcl-language-server server is not running`
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { stdout } = spawnSync(ctx.serverPath, ['--version'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
})
|
||||||
|
const versionString = stdout.slice(`kcl-language-server `.length).trim()
|
||||||
|
|
||||||
|
void vscode.window.showInformationMessage(
|
||||||
|
`kcl-language-server version: ${versionString}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openLogs(ctx: CtxInit): Cmd {
|
||||||
|
return async () => {
|
||||||
|
if (ctx.client.outputChannel) {
|
||||||
|
ctx.client.outputChannel.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
293
rust/kcl-language-server/client/src/config.ts
Normal file
293
rust/kcl-language-server/client/src/config.ts
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import * as Is from 'vscode-languageclient/lib/common/utils/is'
|
||||||
|
import * as os from 'os'
|
||||||
|
import * as path from 'path'
|
||||||
|
import * as vscode from 'vscode'
|
||||||
|
import { log, type Env } from './util'
|
||||||
|
import { expectNotUndefined, unwrapUndefinable } from './undefinable'
|
||||||
|
|
||||||
|
export type RunnableEnvCfgItem = {
|
||||||
|
mask?: string
|
||||||
|
env: Record<string, string>
|
||||||
|
platform?: string | string[]
|
||||||
|
}
|
||||||
|
export type RunnableEnvCfg =
|
||||||
|
| undefined
|
||||||
|
| Record<string, string>
|
||||||
|
| RunnableEnvCfgItem[]
|
||||||
|
|
||||||
|
export class Config {
|
||||||
|
readonly extensionId = 'kittycad.kcl-language-server'
|
||||||
|
configureLang: vscode.Disposable | undefined
|
||||||
|
|
||||||
|
readonly rootSection = 'kcl-language-server'
|
||||||
|
private readonly requiresReloadOpts = ['serverPath', 'server', 'files'].map(
|
||||||
|
(opt) => `${this.rootSection}.${opt}`
|
||||||
|
)
|
||||||
|
|
||||||
|
readonly package: {
|
||||||
|
version: string
|
||||||
|
releaseTag: string | null
|
||||||
|
enableProposedApi: boolean | undefined
|
||||||
|
} = vscode.extensions.getExtension(this.extensionId)!.packageJSON
|
||||||
|
|
||||||
|
readonly globalStorageUri: vscode.Uri
|
||||||
|
|
||||||
|
constructor(ctx: vscode.ExtensionContext) {
|
||||||
|
this.globalStorageUri = ctx.globalStorageUri
|
||||||
|
vscode.workspace.onDidChangeConfiguration(
|
||||||
|
this.onDidChangeConfiguration,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
)
|
||||||
|
this.refreshLogging()
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.configureLang?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
private refreshLogging() {
|
||||||
|
log.setEnabled(this.traceExtension ?? false)
|
||||||
|
log.info('Extension version:', this.package.version)
|
||||||
|
|
||||||
|
const cfg = Object.entries(this.cfg).filter(
|
||||||
|
([_, val]) => !(val instanceof Function)
|
||||||
|
)
|
||||||
|
log.info('Using configuration', Object.fromEntries(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onDidChangeConfiguration(
|
||||||
|
event: vscode.ConfigurationChangeEvent
|
||||||
|
) {
|
||||||
|
this.refreshLogging()
|
||||||
|
|
||||||
|
const requiresReloadOpt = this.requiresReloadOpts.find((opt) =>
|
||||||
|
event.affectsConfiguration(opt)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!requiresReloadOpt) return
|
||||||
|
|
||||||
|
const message = `Changing "${requiresReloadOpt}" requires a server restart`
|
||||||
|
const userResponse = await vscode.window.showInformationMessage(
|
||||||
|
message,
|
||||||
|
'Restart now'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (userResponse) {
|
||||||
|
const command = 'kcl-language-server.restartServer'
|
||||||
|
await vscode.commands.executeCommand(command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't do runtime config validation here for simplicity. More on stackoverflow:
|
||||||
|
// https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension
|
||||||
|
|
||||||
|
private get cfg(): vscode.WorkspaceConfiguration {
|
||||||
|
return vscode.workspace.getConfiguration(this.rootSection)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beware that postfix `!` operator erases both `null` and `undefined`.
|
||||||
|
* This is why the following doesn't work as expected:
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const nullableNum = vscode
|
||||||
|
* .workspace
|
||||||
|
* .getConfiguration
|
||||||
|
* .getConfiguration("kcl-language-server")
|
||||||
|
* .get<number | null>(path)!;
|
||||||
|
*
|
||||||
|
* // What happens is that type of `nullableNum` is `number` but not `null | number`:
|
||||||
|
* const fullFledgedNum: number = nullableNum;
|
||||||
|
* ```
|
||||||
|
* So this getter handles this quirk by not requiring the caller to use postfix `!`
|
||||||
|
*/
|
||||||
|
private get<T>(path: string): T | undefined {
|
||||||
|
return prepareVSCodeConfig(this.cfg.get<T>(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
get serverPath() {
|
||||||
|
return (
|
||||||
|
this.get<null | string>('server.path') ??
|
||||||
|
this.get<null | string>('serverPath')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
get traceExtension() {
|
||||||
|
return this.get<boolean>('trace.extension')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the optional `cb?` parameter is meant to be used to add additional
|
||||||
|
// key/value pairs to the VS Code configuration. This needed for, e.g.,
|
||||||
|
// including a `rust-project.json` into the `linkedProjects` key as part
|
||||||
|
// of the configuration/InitializationParams _without_ causing VS Code
|
||||||
|
// configuration to be written out to workspace-level settings. This is
|
||||||
|
// undesirable behavior because rust-project.json files can be tens of
|
||||||
|
// thousands of lines of JSON, most of which is not meant for humans
|
||||||
|
// to interact with.
|
||||||
|
export function prepareVSCodeConfig<T>(
|
||||||
|
resp: T,
|
||||||
|
cb?: (key: Extract<keyof T, string>, res: { [key: string]: any }) => void
|
||||||
|
): T {
|
||||||
|
if (Is.string(resp)) {
|
||||||
|
return substituteVSCodeVariableInString(resp) as T
|
||||||
|
} else if (resp && Is.array<any>(resp)) {
|
||||||
|
return resp.map((val) => {
|
||||||
|
return prepareVSCodeConfig(val)
|
||||||
|
}) as T
|
||||||
|
} else if (resp && typeof resp === 'object') {
|
||||||
|
const res: { [key: string]: any } = {}
|
||||||
|
for (const key in resp) {
|
||||||
|
const val = resp[key]
|
||||||
|
res[key] = prepareVSCodeConfig(val)
|
||||||
|
if (cb) {
|
||||||
|
cb(key, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res as T
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Merge this with `substituteVSCodeVariables` above
|
||||||
|
export function substituteVariablesInEnv(env: Env): Env {
|
||||||
|
const missingDeps = new Set<string>()
|
||||||
|
// vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
|
||||||
|
// to follow the same convention for our dependency tracking
|
||||||
|
const definedEnvKeys = new Set(Object.keys(env).map((key) => `env:${key}`))
|
||||||
|
const envWithDeps = Object.fromEntries(
|
||||||
|
Object.entries(env).map(([key, value]) => {
|
||||||
|
const deps = new Set<string>()
|
||||||
|
const depRe = new RegExp(/\${(?<depName>.+?)}/g)
|
||||||
|
let match = undefined
|
||||||
|
while ((match = depRe.exec(value))) {
|
||||||
|
const depName = unwrapUndefinable(match.groups?.['depName'])
|
||||||
|
deps.add(depName)
|
||||||
|
// `depName` at this point can have a form of `expression` or
|
||||||
|
// `prefix:expression`
|
||||||
|
if (!definedEnvKeys.has(depName)) {
|
||||||
|
missingDeps.add(depName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [`env:${key}`, { deps: [...deps], value }]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const resolved = new Set<string>()
|
||||||
|
for (const dep of missingDeps) {
|
||||||
|
const match = /(?<prefix>.*?):(?<body>.+)/.exec(dep)
|
||||||
|
if (match) {
|
||||||
|
const { prefix, body } = match.groups!
|
||||||
|
if (prefix === 'env') {
|
||||||
|
const envName = unwrapUndefinable(body)
|
||||||
|
envWithDeps[dep] = {
|
||||||
|
value: process.env[envName] ?? '',
|
||||||
|
deps: [],
|
||||||
|
}
|
||||||
|
resolved.add(dep)
|
||||||
|
} else {
|
||||||
|
// we can't handle other prefixes at the moment
|
||||||
|
// leave values as is, but still mark them as resolved
|
||||||
|
envWithDeps[dep] = {
|
||||||
|
value: '${' + dep + '}',
|
||||||
|
deps: [],
|
||||||
|
}
|
||||||
|
resolved.add(dep)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
envWithDeps[dep] = {
|
||||||
|
value: computeVscodeVar(dep) || '${' + dep + '}',
|
||||||
|
deps: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const toResolve = new Set(Object.keys(envWithDeps))
|
||||||
|
|
||||||
|
let leftToResolveSize
|
||||||
|
do {
|
||||||
|
leftToResolveSize = toResolve.size
|
||||||
|
for (const key of toResolve) {
|
||||||
|
const item = unwrapUndefinable(envWithDeps[key])
|
||||||
|
if (item.deps.every((dep) => resolved.has(dep))) {
|
||||||
|
item.value = item.value.replace(
|
||||||
|
/\${(?<depName>.+?)}/g,
|
||||||
|
(_wholeMatch, depName) => {
|
||||||
|
const item = unwrapUndefinable(envWithDeps[depName])
|
||||||
|
return item.value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
resolved.add(key)
|
||||||
|
toResolve.delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (toResolve.size > 0 && toResolve.size < leftToResolveSize)
|
||||||
|
|
||||||
|
const resolvedEnv: Env = {}
|
||||||
|
for (const key of Object.keys(env)) {
|
||||||
|
const item = unwrapUndefinable(envWithDeps[`env:${key}`])
|
||||||
|
resolvedEnv[key] = item.value
|
||||||
|
}
|
||||||
|
return resolvedEnv
|
||||||
|
}
|
||||||
|
|
||||||
|
const VarRegex = new RegExp(/\$\{(.+?)\}/g)
|
||||||
|
function substituteVSCodeVariableInString(val: string): string {
|
||||||
|
return val.replace(VarRegex, (substring: string, varName) => {
|
||||||
|
if (Is.string(varName)) {
|
||||||
|
return computeVscodeVar(varName) || substring
|
||||||
|
} else {
|
||||||
|
return substring
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeVscodeVar(varName: string): string | null {
|
||||||
|
const workspaceFolder = () => {
|
||||||
|
const folders = vscode.workspace.workspaceFolders ?? []
|
||||||
|
const folder = folders[0]
|
||||||
|
// TODO: support for remote workspaces?
|
||||||
|
const fsPath: string =
|
||||||
|
folder === undefined
|
||||||
|
? // no workspace opened
|
||||||
|
''
|
||||||
|
: // could use currently opened document to detect the correct
|
||||||
|
// workspace. However, that would be determined by the document
|
||||||
|
// user has opened on Editor startup. Could lead to
|
||||||
|
// unpredictable workspace selection in practice.
|
||||||
|
// It's better to pick the first one
|
||||||
|
folder.uri.fsPath
|
||||||
|
return fsPath
|
||||||
|
}
|
||||||
|
// https://code.visualstudio.com/docs/editor/variables-reference
|
||||||
|
const supportedVariables: { [k: string]: () => string } = {
|
||||||
|
workspaceFolder,
|
||||||
|
|
||||||
|
workspaceFolderBasename: () => {
|
||||||
|
return path.basename(workspaceFolder())
|
||||||
|
},
|
||||||
|
|
||||||
|
cwd: () => process.cwd(),
|
||||||
|
userHome: () => os.homedir(),
|
||||||
|
|
||||||
|
// see
|
||||||
|
// https://github.com/microsoft/vscode/blob/08ac1bb67ca2459496b272d8f4a908757f24f56f/src/vs/workbench/api/common/extHostVariableResolverService.ts#L81
|
||||||
|
// or
|
||||||
|
// https://github.com/microsoft/vscode/blob/29eb316bb9f154b7870eb5204ec7f2e7cf649bec/src/vs/server/node/remoteTerminalChannel.ts#L56
|
||||||
|
execPath: () => process.env['VSCODE_EXEC_PATH'] ?? process.execPath,
|
||||||
|
|
||||||
|
pathSeparator: () => path.sep,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varName in supportedVariables) {
|
||||||
|
const fn = expectNotUndefined(
|
||||||
|
supportedVariables[varName],
|
||||||
|
`${varName} should not be undefined here`
|
||||||
|
)
|
||||||
|
return fn()
|
||||||
|
} else {
|
||||||
|
// return "${" + varName + "}";
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
387
rust/kcl-language-server/client/src/ctx.ts
Normal file
387
rust/kcl-language-server/client/src/ctx.ts
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import * as vscode from 'vscode'
|
||||||
|
import type * as lc from 'vscode-languageclient/node'
|
||||||
|
|
||||||
|
import { Config, prepareVSCodeConfig } from './config'
|
||||||
|
import { createClient } from './client'
|
||||||
|
import {
|
||||||
|
isKclDocument,
|
||||||
|
isKclEditor,
|
||||||
|
LazyOutputChannel,
|
||||||
|
log,
|
||||||
|
type KclEditor,
|
||||||
|
} from './util'
|
||||||
|
import type { ServerStatusParams } from './lsp_ext'
|
||||||
|
import { PersistentState } from './persistent_state'
|
||||||
|
import { bootstrap } from './bootstrap'
|
||||||
|
import { TransportKind } from 'vscode-languageclient/node'
|
||||||
|
|
||||||
|
// We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
|
||||||
|
// only those are in use. We use "Empty" to represent these scenarios
|
||||||
|
// (r-a still somewhat works with Live Share, because commands are tunneled to the host)
|
||||||
|
|
||||||
|
export type Workspace =
|
||||||
|
| { kind: 'Empty' }
|
||||||
|
| {
|
||||||
|
kind: 'Workspace Folder'
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'Detached Files'
|
||||||
|
files: vscode.TextDocument[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchWorkspace(): Workspace {
|
||||||
|
const folders = (vscode.workspace.workspaceFolders || []).filter(
|
||||||
|
(folder) => folder.uri.scheme === 'file'
|
||||||
|
)
|
||||||
|
const kclDocuments = vscode.workspace.textDocuments.filter((document) =>
|
||||||
|
isKclDocument(document)
|
||||||
|
)
|
||||||
|
|
||||||
|
return folders.length === 0
|
||||||
|
? kclDocuments.length === 0
|
||||||
|
? { kind: 'Empty' }
|
||||||
|
: {
|
||||||
|
kind: 'Detached Files',
|
||||||
|
files: kclDocuments,
|
||||||
|
}
|
||||||
|
: { kind: 'Workspace Folder' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CommandFactory = {
|
||||||
|
enabled: (ctx: CtxInit) => Cmd
|
||||||
|
disabled?: (ctx: Ctx) => Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CtxInit = Ctx & {
|
||||||
|
readonly client: lc.LanguageClient
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Ctx {
|
||||||
|
readonly statusBar: vscode.StatusBarItem
|
||||||
|
config: Config
|
||||||
|
readonly workspace: Workspace
|
||||||
|
|
||||||
|
private _client: lc.LanguageClient | undefined
|
||||||
|
private _serverPath: string | undefined
|
||||||
|
private traceOutputChannel: vscode.OutputChannel | undefined
|
||||||
|
private outputChannel: vscode.OutputChannel | undefined
|
||||||
|
private clientSubscriptions: Disposable[]
|
||||||
|
private state: PersistentState
|
||||||
|
private commandFactories: Record<string, CommandFactory>
|
||||||
|
private commandDisposables: Disposable[]
|
||||||
|
private lastStatus: ServerStatusParams | { health: 'stopped' } = {
|
||||||
|
health: 'stopped',
|
||||||
|
}
|
||||||
|
|
||||||
|
get client() {
|
||||||
|
return this._client
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly extCtx: vscode.ExtensionContext,
|
||||||
|
commandFactories: Record<string, CommandFactory>,
|
||||||
|
workspace: Workspace
|
||||||
|
) {
|
||||||
|
extCtx.subscriptions.push(this)
|
||||||
|
this.statusBar = vscode.window.createStatusBarItem(
|
||||||
|
vscode.StatusBarAlignment.Left
|
||||||
|
)
|
||||||
|
this.workspace = workspace
|
||||||
|
this.clientSubscriptions = []
|
||||||
|
this.commandDisposables = []
|
||||||
|
this.commandFactories = commandFactories
|
||||||
|
this.state = new PersistentState(extCtx.globalState)
|
||||||
|
this.config = new Config(extCtx)
|
||||||
|
|
||||||
|
this.updateCommands('disable')
|
||||||
|
this.setServerStatus({
|
||||||
|
health: 'stopped',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.config.dispose()
|
||||||
|
this.statusBar.dispose()
|
||||||
|
void this.disposeClient()
|
||||||
|
this.commandDisposables.forEach((disposable) => disposable.dispose())
|
||||||
|
}
|
||||||
|
|
||||||
|
async onWorkspaceFolderChanges() {
|
||||||
|
const workspace = fetchWorkspace()
|
||||||
|
if (
|
||||||
|
workspace.kind === 'Detached Files' &&
|
||||||
|
this.workspace.kind === 'Detached Files'
|
||||||
|
) {
|
||||||
|
if (workspace.files !== this.workspace.files) {
|
||||||
|
if (this.client?.isRunning()) {
|
||||||
|
// Ideally we wouldn't need to tear down the server here, but currently detached files
|
||||||
|
// are only specified at server start
|
||||||
|
await this.stopAndDispose()
|
||||||
|
await this.start()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
workspace.kind === 'Workspace Folder' &&
|
||||||
|
this.workspace.kind === 'Workspace Folder'
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (workspace.kind === 'Empty') {
|
||||||
|
await this.stopAndDispose()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.client?.isRunning()) {
|
||||||
|
await this.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getOrCreateClient() {
|
||||||
|
if (this.workspace.kind === 'Empty') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.traceOutputChannel) {
|
||||||
|
this.traceOutputChannel = new LazyOutputChannel(
|
||||||
|
'KittyCAD Language Server Trace'
|
||||||
|
)
|
||||||
|
this.pushExtCleanup(this.traceOutputChannel)
|
||||||
|
}
|
||||||
|
if (!this.outputChannel) {
|
||||||
|
this.outputChannel = vscode.window.createOutputChannel(
|
||||||
|
'KittyCAD Language Server'
|
||||||
|
)
|
||||||
|
this.pushExtCleanup(this.outputChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._client) {
|
||||||
|
this._serverPath = await bootstrap(
|
||||||
|
this.extCtx,
|
||||||
|
this.config,
|
||||||
|
this.state
|
||||||
|
).catch((err) => {
|
||||||
|
let message = 'bootstrap error. '
|
||||||
|
|
||||||
|
message +=
|
||||||
|
'See the logs in "OUTPUT > KittyCAD Language Client" (should open automatically). '
|
||||||
|
message +=
|
||||||
|
'To enable verbose logs use { "kcl-language-server.trace.extension": true }'
|
||||||
|
|
||||||
|
log.error('Bootstrap error', err)
|
||||||
|
throw new Error(message)
|
||||||
|
})
|
||||||
|
const run: lc.Executable = {
|
||||||
|
command: this._serverPath,
|
||||||
|
args: ['--json', 'server'],
|
||||||
|
transport: TransportKind.stdio,
|
||||||
|
options: { env: { ...process.env } },
|
||||||
|
}
|
||||||
|
const serverOptions = {
|
||||||
|
run,
|
||||||
|
debug: run,
|
||||||
|
}
|
||||||
|
|
||||||
|
let rawInitializationOptions = vscode.workspace.getConfiguration(
|
||||||
|
'kcl-language-server'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (this.workspace.kind === 'Detached Files') {
|
||||||
|
rawInitializationOptions = {
|
||||||
|
detachedFiles: this.workspace.files.map((file) => file.uri.fsPath),
|
||||||
|
...rawInitializationOptions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initializationOptions = prepareVSCodeConfig(
|
||||||
|
rawInitializationOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
this._client = await createClient(
|
||||||
|
this.traceOutputChannel,
|
||||||
|
this.outputChannel,
|
||||||
|
initializationOptions,
|
||||||
|
serverOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return this._client
|
||||||
|
}
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
log.info('Starting language client')
|
||||||
|
const client = await this.getOrCreateClient()
|
||||||
|
if (!client) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await client.start()
|
||||||
|
this.setServerStatus({ health: 'ok', quiescent: true })
|
||||||
|
this.updateCommands()
|
||||||
|
}
|
||||||
|
|
||||||
|
async restart() {
|
||||||
|
// FIXME: We should reuse the client, that is ctx.deactivate() if none of the configs have changed
|
||||||
|
await this.stopAndDispose()
|
||||||
|
await this.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop() {
|
||||||
|
if (!this._client) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.info('Stopping language client')
|
||||||
|
this.updateCommands('disable')
|
||||||
|
await this._client.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopAndDispose() {
|
||||||
|
if (!this._client) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.info('Disposing language client')
|
||||||
|
this.updateCommands('disable')
|
||||||
|
await this.disposeClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
private async disposeClient() {
|
||||||
|
this.clientSubscriptions?.forEach((disposable) => disposable.dispose())
|
||||||
|
this.clientSubscriptions = []
|
||||||
|
try {
|
||||||
|
await this._client?.dispose(2000)
|
||||||
|
} catch (e) {
|
||||||
|
// DO nothing.
|
||||||
|
}
|
||||||
|
this._serverPath = undefined
|
||||||
|
this._client = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
get activeKclEditor(): KclEditor | undefined {
|
||||||
|
const editor = vscode.window.activeTextEditor
|
||||||
|
return editor && isKclEditor(editor) ? editor : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
get extensionPath(): string {
|
||||||
|
return this.extCtx.extensionPath
|
||||||
|
}
|
||||||
|
|
||||||
|
get subscriptions(): Disposable[] {
|
||||||
|
return this.extCtx.subscriptions
|
||||||
|
}
|
||||||
|
|
||||||
|
get serverPath(): string | undefined {
|
||||||
|
return this._serverPath
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateCommands(forceDisable?: 'disable') {
|
||||||
|
this.commandDisposables.forEach((disposable) => disposable.dispose())
|
||||||
|
this.commandDisposables = []
|
||||||
|
|
||||||
|
const clientRunning = (!forceDisable && this._client?.isRunning()) ?? false
|
||||||
|
const isClientRunning = function (_ctx: Ctx): _ctx is CtxInit {
|
||||||
|
return clientRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [name, factory] of Object.entries(this.commandFactories)) {
|
||||||
|
const fullName = `kcl-language-server.${name}`
|
||||||
|
let callback
|
||||||
|
if (isClientRunning(this)) {
|
||||||
|
// we asserted that `client` is defined
|
||||||
|
callback = factory.enabled(this)
|
||||||
|
} else if (factory.disabled) {
|
||||||
|
callback = factory.disabled(this)
|
||||||
|
} else {
|
||||||
|
callback = () =>
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
`command ${fullName} failed: kcl-language-server server is not running`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.commandDisposables.push(
|
||||||
|
vscode.commands.registerCommand(fullName, callback)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setServerStatus(status: ServerStatusParams | { health: 'stopped' }) {
|
||||||
|
this.lastStatus = status
|
||||||
|
this.updateStatusBarItem()
|
||||||
|
}
|
||||||
|
refreshServerStatus() {
|
||||||
|
this.updateStatusBarItem()
|
||||||
|
}
|
||||||
|
private updateStatusBarItem() {
|
||||||
|
let icon = ''
|
||||||
|
const status = this.lastStatus
|
||||||
|
const statusBar = this.statusBar
|
||||||
|
statusBar.show()
|
||||||
|
statusBar.tooltip = new vscode.MarkdownString('', true)
|
||||||
|
statusBar.tooltip.isTrusted = true
|
||||||
|
switch (status.health) {
|
||||||
|
case 'ok':
|
||||||
|
statusBar.tooltip.appendText(status.message ?? 'Ready')
|
||||||
|
statusBar.color = undefined
|
||||||
|
statusBar.backgroundColor = undefined
|
||||||
|
statusBar.command = 'kcl-language-server.openLogs'
|
||||||
|
break
|
||||||
|
case 'warning':
|
||||||
|
if (status.message) {
|
||||||
|
statusBar.tooltip.appendText(status.message)
|
||||||
|
}
|
||||||
|
statusBar.color = new vscode.ThemeColor(
|
||||||
|
'statusBarItem.warningForeground'
|
||||||
|
)
|
||||||
|
statusBar.backgroundColor = new vscode.ThemeColor(
|
||||||
|
'statusBarItem.warningBackground'
|
||||||
|
)
|
||||||
|
statusBar.command = 'kcl-language-server.openLogs'
|
||||||
|
icon = '$(warning) '
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
if (status.message) {
|
||||||
|
statusBar.tooltip.appendText(status.message)
|
||||||
|
}
|
||||||
|
statusBar.color = new vscode.ThemeColor('statusBarItem.errorForeground')
|
||||||
|
statusBar.backgroundColor = new vscode.ThemeColor(
|
||||||
|
'statusBarItem.errorBackground'
|
||||||
|
)
|
||||||
|
statusBar.command = 'kcl-language-server.openLogs'
|
||||||
|
icon = '$(error) '
|
||||||
|
break
|
||||||
|
case 'stopped':
|
||||||
|
statusBar.tooltip.appendText('Server is stopped')
|
||||||
|
statusBar.tooltip.appendMarkdown(
|
||||||
|
'\n\n[Start server](command:kcl-language-server.startServer)'
|
||||||
|
)
|
||||||
|
statusBar.color = new vscode.ThemeColor(
|
||||||
|
'statusBarItem.warningForeground'
|
||||||
|
)
|
||||||
|
statusBar.backgroundColor = new vscode.ThemeColor(
|
||||||
|
'statusBarItem.warningBackground'
|
||||||
|
)
|
||||||
|
statusBar.command = 'kcl-language-server.startServer'
|
||||||
|
statusBar.text = '$(stop-circle) kcl-language-server'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (statusBar.tooltip.value) {
|
||||||
|
statusBar.tooltip.appendMarkdown('\n\n---\n\n')
|
||||||
|
}
|
||||||
|
statusBar.tooltip.appendMarkdown(
|
||||||
|
'\n\n[Restart server](command:kcl-language-server.restartServer)'
|
||||||
|
)
|
||||||
|
statusBar.tooltip.appendMarkdown(
|
||||||
|
'\n\n[Stop server](command:kcl-language-server.stopServer)'
|
||||||
|
)
|
||||||
|
if (!status.quiescent) icon = '$(sync~spin) '
|
||||||
|
statusBar.text = `${icon}kcl-language-server`
|
||||||
|
}
|
||||||
|
|
||||||
|
pushExtCleanup(d: Disposable) {
|
||||||
|
this.extCtx.subscriptions.push(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Disposable {
|
||||||
|
dispose(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Cmd = (...args: any[]) => unknown
|
24
rust/kcl-language-server/client/src/lsp_ext.ts
Normal file
24
rust/kcl-language-server/client/src/lsp_ext.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import * as lc from 'vscode-languageclient'
|
||||||
|
|
||||||
|
export type CommandLink = {
|
||||||
|
/**
|
||||||
|
* A tooltip for the command, when represented in the UI.
|
||||||
|
*/
|
||||||
|
tooltip?: string
|
||||||
|
} & lc.Command
|
||||||
|
export type CommandLinkGroup = {
|
||||||
|
title?: string
|
||||||
|
commands: CommandLink[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// experimental extensions
|
||||||
|
|
||||||
|
export const serverStatus = new lc.NotificationType<ServerStatusParams>(
|
||||||
|
'experimental/serverStatus'
|
||||||
|
)
|
||||||
|
export type ServerStatusParams = {
|
||||||
|
health: 'ok' | 'warning' | 'error'
|
||||||
|
quiescent: boolean
|
||||||
|
message?: string
|
||||||
|
}
|
79
rust/kcl-language-server/client/src/main.ts
Normal file
79
rust/kcl-language-server/client/src/main.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import * as vscode from 'vscode'
|
||||||
|
import type * as lc from 'vscode-languageclient/node'
|
||||||
|
|
||||||
|
import * as commands from './commands'
|
||||||
|
import { type CommandFactory, Ctx, fetchWorkspace } from './ctx'
|
||||||
|
import { setContextValue } from './util'
|
||||||
|
|
||||||
|
const KCL_PROJECT_CONTEXT_NAME = 'inKclProject'
|
||||||
|
|
||||||
|
export interface KclAnalyzerExtensionApi {
|
||||||
|
readonly client?: lc.LanguageClient
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deactivate() {
|
||||||
|
await setContextValue(KCL_PROJECT_CONTEXT_NAME, undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function activate(
|
||||||
|
context: vscode.ExtensionContext
|
||||||
|
): Promise<KclAnalyzerExtensionApi> {
|
||||||
|
const ctx = new Ctx(context, createCommands(), fetchWorkspace())
|
||||||
|
// VS Code doesn't show a notification when an extension fails to activate
|
||||||
|
// so we do it ourselves.
|
||||||
|
const api = await activateServer(ctx).catch((err) => {
|
||||||
|
void vscode.window.showErrorMessage(
|
||||||
|
`Cannot activate kcl-language-server extension: ${err.message}`
|
||||||
|
)
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
await setContextValue(KCL_PROJECT_CONTEXT_NAME, true)
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
|
||||||
|
async function activateServer(ctx: Ctx): Promise<KclAnalyzerExtensionApi> {
|
||||||
|
await ctx.start()
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCommands(): Record<string, CommandFactory> {
|
||||||
|
return {
|
||||||
|
restartServer: {
|
||||||
|
enabled: (ctx) => async () => {
|
||||||
|
await ctx.restart()
|
||||||
|
},
|
||||||
|
disabled: (ctx) => async () => {
|
||||||
|
await ctx.start()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
startServer: {
|
||||||
|
enabled: (ctx) => async () => {
|
||||||
|
await ctx.start()
|
||||||
|
ctx.setServerStatus({
|
||||||
|
health: 'ok',
|
||||||
|
quiescent: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
disabled: (ctx) => async () => {
|
||||||
|
await ctx.start()
|
||||||
|
ctx.setServerStatus({
|
||||||
|
health: 'ok',
|
||||||
|
quiescent: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stopServer: {
|
||||||
|
enabled: (ctx) => async () => {
|
||||||
|
// FIXME: We should reuse the client, that is ctx.deactivate() if none of the configs have changed
|
||||||
|
await ctx.stopAndDispose()
|
||||||
|
ctx.setServerStatus({
|
||||||
|
health: 'stopped',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
disabled: (_) => async () => {},
|
||||||
|
},
|
||||||
|
serverVersion: { enabled: commands.serverVersion },
|
||||||
|
openLogs: { enabled: commands.openLogs },
|
||||||
|
}
|
||||||
|
}
|
21
rust/kcl-language-server/client/src/persistent_state.ts
Normal file
21
rust/kcl-language-server/client/src/persistent_state.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import type * as vscode from 'vscode'
|
||||||
|
import { log } from './util'
|
||||||
|
|
||||||
|
export class PersistentState {
|
||||||
|
constructor(private readonly globalState: vscode.Memento) {
|
||||||
|
const { serverVersion } = this
|
||||||
|
log.info('PersistentState:', { serverVersion })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version of the extension that installed the server.
|
||||||
|
* Used to check if we need to run patchelf again on NixOS.
|
||||||
|
*/
|
||||||
|
get serverVersion(): string | undefined {
|
||||||
|
return this.globalState.get('serverVersion')
|
||||||
|
}
|
||||||
|
async updateServerVersion(value: string | undefined) {
|
||||||
|
await this.globalState.update('serverVersion', value)
|
||||||
|
}
|
||||||
|
}
|
25
rust/kcl-language-server/client/src/test/runTest.ts
Normal file
25
rust/kcl-language-server/client/src/test/runTest.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import * as path from 'path'
|
||||||
|
|
||||||
|
import { runTests } from '@vscode/test-electron'
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
// The folder containing the Extension Manifest package.json
|
||||||
|
// Passed to `--extensionDevelopmentPath`
|
||||||
|
const extensionDevelopmentPath = path.resolve(__dirname, '../../')
|
||||||
|
|
||||||
|
// The path to the extension test runner script
|
||||||
|
// Passed to --extensionTestsPath
|
||||||
|
const extensionTestsPath = path.resolve(__dirname, './suite/index')
|
||||||
|
|
||||||
|
// Download VS Code, unzip it and run the integration test
|
||||||
|
await runTests({ extensionDevelopmentPath, extensionTestsPath })
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
console.error('Failed to run tests')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
main()
|
@ -0,0 +1,16 @@
|
|||||||
|
import * as assert from 'assert'
|
||||||
|
|
||||||
|
// You can import and use all API from the 'vscode' module
|
||||||
|
// as well as import your extension to test it
|
||||||
|
import * as vscode from 'vscode'
|
||||||
|
// import * as myExtension from '../../extension';
|
||||||
|
|
||||||
|
suite('Extension Test Suite', () => {
|
||||||
|
/* eslint-disable */
|
||||||
|
vscode.window.showInformationMessage('Start all tests.')
|
||||||
|
|
||||||
|
test('Sample test', () => {
|
||||||
|
assert.strictEqual([1, 2, 3].indexOf(5), -1)
|
||||||
|
assert.strictEqual([1, 2, 3].indexOf(0), -1)
|
||||||
|
})
|
||||||
|
})
|
33
rust/kcl-language-server/client/src/test/suite/index.ts
Normal file
33
rust/kcl-language-server/client/src/test/suite/index.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import * as path from 'path'
|
||||||
|
const Mocha = require('mocha')
|
||||||
|
const { glob } = require('glob')
|
||||||
|
|
||||||
|
export function run(): Promise<void> {
|
||||||
|
// Create the mocha test
|
||||||
|
const mocha = new Mocha({
|
||||||
|
ui: 'tdd',
|
||||||
|
})
|
||||||
|
|
||||||
|
const testsRoot = path.resolve(__dirname, '..')
|
||||||
|
|
||||||
|
return new Promise((c, e) => {
|
||||||
|
glob('**/**.test.js', { cwd: testsRoot }).then((files: string[]) => {
|
||||||
|
// Add files to the test suite
|
||||||
|
files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)))
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Run the mocha test
|
||||||
|
mocha.run((failures: any) => {
|
||||||
|
if (failures > 0) {
|
||||||
|
e(new Error(`${failures} tests failed.`))
|
||||||
|
} else {
|
||||||
|
c()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
e(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
23
rust/kcl-language-server/client/src/undefinable.ts
Normal file
23
rust/kcl-language-server/client/src/undefinable.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
export type NotUndefined<T> = T extends undefined ? never : T
|
||||||
|
|
||||||
|
export type Undefinable<T> = T | undefined
|
||||||
|
|
||||||
|
function isNotUndefined<T>(input: Undefinable<T>): input is NotUndefined<T> {
|
||||||
|
return input !== undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export function expectNotUndefined<T>(
|
||||||
|
input: Undefinable<T>,
|
||||||
|
msg: string
|
||||||
|
): NotUndefined<T> {
|
||||||
|
if (isNotUndefined(input)) {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TypeError(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unwrapUndefinable<T>(input: Undefinable<T>): NotUndefined<T> {
|
||||||
|
return expectNotUndefined(input, `unwrapping \`undefined\``)
|
||||||
|
}
|
240
rust/kcl-language-server/client/src/util.ts
Normal file
240
rust/kcl-language-server/client/src/util.ts
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
/* eslint suggest-no-throw/suggest-no-throw: 0 */
|
||||||
|
import * as vscode from 'vscode'
|
||||||
|
import { strict as nativeAssert } from 'assert'
|
||||||
|
import { exec, type ExecOptions, spawnSync } from 'child_process'
|
||||||
|
import { inspect } from 'util'
|
||||||
|
|
||||||
|
export interface Env {
|
||||||
|
[name: string]: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assert(
|
||||||
|
condition: boolean,
|
||||||
|
explanation: string
|
||||||
|
): asserts condition {
|
||||||
|
try {
|
||||||
|
nativeAssert(condition, explanation)
|
||||||
|
} catch (err) {
|
||||||
|
log.error(`Assertion failed:`, explanation)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
private enabled = true
|
||||||
|
private readonly output = vscode.window.createOutputChannel(
|
||||||
|
'KittyCAD Language Client'
|
||||||
|
)
|
||||||
|
|
||||||
|
setEnabled(yes: boolean): void {
|
||||||
|
log.enabled = yes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hint: the type [T, ...T[]] means a non-empty array
|
||||||
|
debug(...msg: [unknown, ...unknown[]]): void {
|
||||||
|
if (!log.enabled) return
|
||||||
|
log.write('DEBUG', ...msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
info(...msg: [unknown, ...unknown[]]): void {
|
||||||
|
log.write('INFO', ...msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(...msg: [unknown, ...unknown[]]): void {
|
||||||
|
debugger
|
||||||
|
log.write('WARN', ...msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
error(...msg: [unknown, ...unknown[]]): void {
|
||||||
|
debugger
|
||||||
|
log.write('ERROR', ...msg)
|
||||||
|
log.output.show(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private write(label: string, ...messageParts: unknown[]): void {
|
||||||
|
const message = messageParts.map(log.stringify).join(' ')
|
||||||
|
const dateTime = new Date().toLocaleString()
|
||||||
|
log.output.appendLine(`${label} [${dateTime}]: ${message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
private stringify(val: unknown): string {
|
||||||
|
if (typeof val === 'string') return val
|
||||||
|
return inspect(val, {
|
||||||
|
colors: false,
|
||||||
|
depth: 6, // heuristic
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const log = new Logger()
|
||||||
|
|
||||||
|
export function sleep(ms: number) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||||
|
}
|
||||||
|
|
||||||
|
export type KclDocument = vscode.TextDocument & { languageId: 'kcl' }
|
||||||
|
export type KclEditor = vscode.TextEditor & { document: KclDocument }
|
||||||
|
|
||||||
|
export function isKclDocument(
|
||||||
|
document: vscode.TextDocument
|
||||||
|
): document is KclDocument {
|
||||||
|
// Prevent corrupted text (particularly via inlay hints) in diff views
|
||||||
|
// by allowing only `file` schemes
|
||||||
|
// unfortunately extensions that use diff views not always set this
|
||||||
|
// to something different than 'file' (see ongoing bug: #4608)
|
||||||
|
return document.languageId === 'kcl' && document.uri.scheme === 'file'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCargoTomlDocument(
|
||||||
|
document: vscode.TextDocument
|
||||||
|
): document is KclDocument {
|
||||||
|
// ideally `document.languageId` should be 'toml' but user maybe not have toml extension installed
|
||||||
|
return (
|
||||||
|
document.uri.scheme === 'file' && document.fileName.endsWith('Cargo.toml')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isKclEditor(editor: vscode.TextEditor): editor is KclEditor {
|
||||||
|
return isKclDocument(editor.document)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDocumentInWorkspace(document: KclDocument): boolean {
|
||||||
|
const workspaceFolders = vscode.workspace.workspaceFolders
|
||||||
|
if (!workspaceFolders) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for (const folder of workspaceFolders) {
|
||||||
|
if (document.uri.fsPath.startsWith(folder.uri.fsPath)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isValidExecutable(path: string): boolean {
|
||||||
|
log.debug('Checking availability of a binary at', path)
|
||||||
|
|
||||||
|
const res = spawnSync(path, ['--version'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
env: { ...process.env },
|
||||||
|
})
|
||||||
|
|
||||||
|
const printOutput = res.error ? log.warn : log.info
|
||||||
|
printOutput(path, '--version:', res)
|
||||||
|
|
||||||
|
return res.status === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets ['when'](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts) clause contexts */
|
||||||
|
export function setContextValue(key: string, value: any): Thenable<void> {
|
||||||
|
return vscode.commands.executeCommand('setContext', key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a higher-order function that caches the results of invoking the
|
||||||
|
* underlying async function.
|
||||||
|
*/
|
||||||
|
export function memoizeAsync<Ret, TThis, Param extends string>(
|
||||||
|
func: (this: TThis, arg: Param) => Promise<Ret>
|
||||||
|
) {
|
||||||
|
const cache = new Map<string, Ret>()
|
||||||
|
|
||||||
|
return async function (this: TThis, arg: Param) {
|
||||||
|
const cached = cache.get(arg)
|
||||||
|
if (cached) return cached
|
||||||
|
|
||||||
|
const result = await func.call(this, arg)
|
||||||
|
cache.set(arg, result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Awaitable wrapper around `child_process.exec` */
|
||||||
|
export function execute(
|
||||||
|
command: string,
|
||||||
|
options: ExecOptions
|
||||||
|
): Promise<string> {
|
||||||
|
log.info(`running command: ${command}`)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(command, options, (err, stdout, stderr) => {
|
||||||
|
if (err) {
|
||||||
|
log.error(err)
|
||||||
|
reject(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
reject(new Error(stderr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(stdout.trimEnd())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function executeDiscoverProject(
|
||||||
|
command: string,
|
||||||
|
options: ExecOptions
|
||||||
|
): Promise<string> {
|
||||||
|
options = Object.assign({ maxBuffer: 10 * 1024 * 1024 }, options)
|
||||||
|
log.info(`running command: ${command}`)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(command, options, (err, stdout, _) => {
|
||||||
|
if (err) {
|
||||||
|
log.error(err)
|
||||||
|
reject(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(stdout.trimEnd())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LazyOutputChannel implements vscode.OutputChannel {
|
||||||
|
constructor(name: string) {
|
||||||
|
this.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
name: string
|
||||||
|
_channel: vscode.OutputChannel | undefined
|
||||||
|
|
||||||
|
get channel(): vscode.OutputChannel {
|
||||||
|
if (!this._channel) {
|
||||||
|
this._channel = vscode.window.createOutputChannel(this.name)
|
||||||
|
}
|
||||||
|
return this._channel
|
||||||
|
}
|
||||||
|
|
||||||
|
append(value: string): void {
|
||||||
|
this.channel.append(value)
|
||||||
|
}
|
||||||
|
appendLine(value: string): void {
|
||||||
|
this.channel.appendLine(value)
|
||||||
|
}
|
||||||
|
replace(value: string): void {
|
||||||
|
this.channel.replace(value)
|
||||||
|
}
|
||||||
|
clear(): void {
|
||||||
|
if (this._channel) {
|
||||||
|
this._channel.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
show(preserveFocus?: boolean): void
|
||||||
|
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void
|
||||||
|
show(column?: any, preserveFocus?: any): void {
|
||||||
|
this.channel.show(column, preserveFocus)
|
||||||
|
}
|
||||||
|
hide(): void {
|
||||||
|
if (this._channel) {
|
||||||
|
this._channel.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dispose(): void {
|
||||||
|
if (this._channel) {
|
||||||
|
this._channel.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
rust/kcl-language-server/icon.png
Normal file
BIN
rust/kcl-language-server/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
151
rust/kcl-language-server/package.json
Normal file
151
rust/kcl-language-server/package.json
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
{
|
||||||
|
"name": "kcl-language-server",
|
||||||
|
"displayName": "KittyCAD Language Server",
|
||||||
|
"description": "KittyCAD language support for Visual Studio Code",
|
||||||
|
"private": true,
|
||||||
|
"icon": "icon.png",
|
||||||
|
"publisher": "kittycad",
|
||||||
|
"homepage": "https://kittycad.io",
|
||||||
|
"license": "MIT",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"releaseTag": null,
|
||||||
|
"keywords": [
|
||||||
|
"language-server",
|
||||||
|
"kittycad",
|
||||||
|
"kcl",
|
||||||
|
"hardware",
|
||||||
|
"cad",
|
||||||
|
"manufacturing"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"Programming Languages"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "https://github.com/kittycad/modeling-app.git",
|
||||||
|
"type": "git"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.97.0"
|
||||||
|
},
|
||||||
|
"enabledApiProposals": [],
|
||||||
|
"activationEvents": [
|
||||||
|
"onLanguage:kcl"
|
||||||
|
],
|
||||||
|
"main": "./dist/main.js",
|
||||||
|
"contributes": {
|
||||||
|
"languages": [
|
||||||
|
{
|
||||||
|
"id": "kcl",
|
||||||
|
"extensions": [
|
||||||
|
".kcl"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "kcl-language-server",
|
||||||
|
"properties": {
|
||||||
|
"kcl-language-server.server.path": {
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"scope": "machine-overridable",
|
||||||
|
"default": null,
|
||||||
|
"markdownDescription": "Path to kcl-language-server executable (points to bundled binary by default)."
|
||||||
|
},
|
||||||
|
"kcl-language-server.trace.server": {
|
||||||
|
"type": "string",
|
||||||
|
"scope": "window",
|
||||||
|
"enum": [
|
||||||
|
"off",
|
||||||
|
"messages",
|
||||||
|
"verbose"
|
||||||
|
],
|
||||||
|
"enumDescriptions": [
|
||||||
|
"No traces",
|
||||||
|
"Error only",
|
||||||
|
"Full log"
|
||||||
|
],
|
||||||
|
"default": "off",
|
||||||
|
"description": "Trace requests to the kcl-language-server (this is usually overly verbose and not recommended for regular users)."
|
||||||
|
},
|
||||||
|
"kcl-language-server.trace.extension": {
|
||||||
|
"description": "Enable logging of VS Code extensions itself.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configurationDefaults": {
|
||||||
|
"[kcl]": {
|
||||||
|
"editor.semanticHighlighting.enabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "kcl-language-server.restartServer",
|
||||||
|
"title": "Restart server",
|
||||||
|
"category": "kcl-language-server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "kcl-language-server.startServer",
|
||||||
|
"title": "Start server",
|
||||||
|
"category": "kcl-language-server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "kcl-language-server.stopServer",
|
||||||
|
"title": "Stop server",
|
||||||
|
"category": "kcl-language-server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "kcl-language-server.serverVersion",
|
||||||
|
"title": "Show server version",
|
||||||
|
"category": "kcl-language-server"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"menus": {
|
||||||
|
"commandPalette": [
|
||||||
|
{
|
||||||
|
"command": "kcl-language-server.restartServer",
|
||||||
|
"when": "inKclProject"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "kcl-language-server.serverVersion",
|
||||||
|
"when": "inKclProject"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"vscode:prepublish": "yarn run build-base -- --minify",
|
||||||
|
"deploy": "vsce publish --yarn",
|
||||||
|
"build-base": "esbuild ./client/src/main.ts --bundle --outfile=dist/main.js --external:vscode --format=cjs --platform=node --target=node16",
|
||||||
|
"test-compile": "tsc -p ./",
|
||||||
|
"compile": "cross-env NODE_ENV=production tsc -b",
|
||||||
|
"build": "yarn run build-base -- --sourcemap",
|
||||||
|
"watch": "yarn run build-base -- --sourcemap --watch",
|
||||||
|
"pretest": "yarn run build && yarn test-compile",
|
||||||
|
"test": "node ./dist/client/src/test/runTest.js",
|
||||||
|
"package": "vsce package -o kcl-language-server.vsix"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tsconfig/strictest": "^2.0.5",
|
||||||
|
"@types/glob": "^8.1.0",
|
||||||
|
"@types/mocha": "^10.0.10",
|
||||||
|
"@types/node": "^22.13.9",
|
||||||
|
"@types/vscode": "^1.97.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.6.0",
|
||||||
|
"@typescript-eslint/parser": "^6.6.0",
|
||||||
|
"@vscode/test-electron": "^2.4.1",
|
||||||
|
"@vscode/vsce": "^2.30.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"esbuild": "^0.25.0",
|
||||||
|
"glob": "^10.4.3",
|
||||||
|
"mocha": "^11.1.0",
|
||||||
|
"typescript": "^5.8.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-languageclient": "^9.0.1"
|
||||||
|
}
|
||||||
|
}
|
180
rust/kcl-language-server/src/main.rs
Normal file
180
rust/kcl-language-server/src/main.rs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
//! The `kcl` lsp server.
|
||||||
|
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use clap::Parser;
|
||||||
|
use slog::Drain;
|
||||||
|
use tower_lsp::{LspService, Server as LspServer};
|
||||||
|
use tracing_subscriber::{prelude::*, Layer};
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
/// Initialize the logger.
|
||||||
|
// We need a slog::Logger for steno and when we export out the logs from re-exec-ed processes.
|
||||||
|
pub static ref LOGGER: slog::Logger = {
|
||||||
|
let decorator = slog_term::TermDecorator::new().build();
|
||||||
|
let drain = slog_term::FullFormat::new(decorator).build().fuse();
|
||||||
|
let drain = slog_async::Async::new(drain).build().fuse();
|
||||||
|
slog::Logger::root(drain, slog::slog_o!())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This doc string acts as a help message when the user runs '--help'
|
||||||
|
/// as do all doc strings on fields.
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
#[clap(version = clap::crate_version!(), author = clap::crate_authors!("\n"))]
|
||||||
|
pub struct Opts {
|
||||||
|
/// Print debug info
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub debug: bool,
|
||||||
|
|
||||||
|
/// Print logs as json
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub json: bool,
|
||||||
|
|
||||||
|
/// The subcommand to run.
|
||||||
|
#[clap(subcommand)]
|
||||||
|
pub subcmd: SubCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Opts {
|
||||||
|
/// Setup our logger.
|
||||||
|
pub fn create_logger(&self) -> slog::Logger {
|
||||||
|
if self.json {
|
||||||
|
let drain = slog_json::Json::default(std::io::stderr()).fuse();
|
||||||
|
self.async_root_logger(drain)
|
||||||
|
} else {
|
||||||
|
let decorator = slog_term::TermDecorator::new().build();
|
||||||
|
let drain = slog_term::FullFormat::new(decorator).build().fuse();
|
||||||
|
self.async_root_logger(drain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn async_root_logger<T>(&self, drain: T) -> slog::Logger
|
||||||
|
where
|
||||||
|
T: slog::Drain + Send + 'static,
|
||||||
|
<T as slog::Drain>::Err: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
let level = if self.debug {
|
||||||
|
slog::Level::Debug
|
||||||
|
} else {
|
||||||
|
slog::Level::Info
|
||||||
|
};
|
||||||
|
|
||||||
|
let level_drain = slog::LevelFilter(drain, level).fuse();
|
||||||
|
let async_drain = slog_async::Async::new(level_drain).build().fuse();
|
||||||
|
slog::Logger::root(async_drain, slog::o!())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A subcommand for our cli.
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
pub enum SubCommand {
|
||||||
|
/// Run the server.
|
||||||
|
Server(kcl_lib::KclLspServerSubCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
let opts: Opts = Opts::parse();
|
||||||
|
|
||||||
|
let level_filter = if opts.debug {
|
||||||
|
tracing_subscriber::filter::LevelFilter::DEBUG
|
||||||
|
} else {
|
||||||
|
tracing_subscriber::filter::LevelFilter::INFO
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format fields using the provided closure.
|
||||||
|
// We want to make this very consise otherwise the logs are not able to be read by humans.
|
||||||
|
let format = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| {
|
||||||
|
if format!("{}", field) == "message" {
|
||||||
|
write!(writer, "{}: {:?}", field, value)
|
||||||
|
} else {
|
||||||
|
write!(writer, "{}", field)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Separate each field with a comma.
|
||||||
|
// This method is provided by an extension trait in the
|
||||||
|
// `tracing-subscriber` prelude.
|
||||||
|
.delimited(", ");
|
||||||
|
|
||||||
|
let (json, plain) = if opts.json {
|
||||||
|
// Cloud run likes json formatted logs if possible.
|
||||||
|
// See: https://cloud.google.com/run/docs/logging
|
||||||
|
// We could probably format these specifically for cloud run if we wanted,
|
||||||
|
// will save that as a TODO: https://cloud.google.com/run/docs/logging#special-fields
|
||||||
|
(
|
||||||
|
Some(tracing_subscriber::fmt::layer().json().with_filter(level_filter)),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
Some(
|
||||||
|
tracing_subscriber::fmt::layer()
|
||||||
|
.pretty()
|
||||||
|
.fmt_fields(format)
|
||||||
|
.with_filter(level_filter),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize the tracing.
|
||||||
|
tracing_subscriber::registry().with(json).with(plain).init();
|
||||||
|
|
||||||
|
if let Err(err) = run_cmd(&opts).await {
|
||||||
|
bail!("running cmd `{:?}` failed: {:?}", &opts.subcmd, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_cmd(opts: &Opts) -> Result<()> {
|
||||||
|
match &opts.subcmd {
|
||||||
|
SubCommand::Server(s) => {
|
||||||
|
let (service, socket) = LspService::new(|client| {
|
||||||
|
kcl_lib::KclLspBackend::new(client, Default::default(), kittycad::Client::new(""), false).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO find a way to ctrl+c on windows.
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
{
|
||||||
|
// For Cloud run & ctrl+c, shutdown gracefully.
|
||||||
|
// "The main process inside the container will receive SIGTERM, and after a grace period,
|
||||||
|
// SIGKILL."
|
||||||
|
// Registering SIGKILL here will panic at runtime, so let's avoid that.
|
||||||
|
use signal_hook::{
|
||||||
|
consts::{SIGINT, SIGTERM},
|
||||||
|
iterator::Signals,
|
||||||
|
};
|
||||||
|
let mut signals = Signals::new([SIGINT, SIGTERM])?;
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
if let Some(sig) = signals.forever().next() {
|
||||||
|
log::info!("received signal: {:?}", sig);
|
||||||
|
log::info!("triggering cleanup...");
|
||||||
|
|
||||||
|
// Exit the process.
|
||||||
|
log::info!("all clean, exiting!");
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.stdio {
|
||||||
|
// Listen on stdin and stdout.
|
||||||
|
let stdin = tokio::io::stdin();
|
||||||
|
let stdout = tokio::io::stdout();
|
||||||
|
LspServer::new(stdin, stdout, socket).serve(service).await;
|
||||||
|
} else {
|
||||||
|
// Listen on a tcp stream.
|
||||||
|
let listener = tokio::net::TcpListener::bind(&format!("0.0.0.0:{}", s.socket)).await?;
|
||||||
|
let (stream, _) = listener.accept().await?;
|
||||||
|
let (read, write) = tokio::io::split(stream);
|
||||||
|
LspServer::new(read, write, socket).serve(service).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
20
rust/kcl-language-server/tsconfig.json
Normal file
20
rust/kcl-language-server/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/strictest/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": false,
|
||||||
|
"module": "node16",
|
||||||
|
"moduleResolution": "node16",
|
||||||
|
"target": "es2021",
|
||||||
|
"outDir": "dist",
|
||||||
|
"lib": ["es2021"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"rootDir": ".",
|
||||||
|
"newLine": "LF",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
|
||||||
|
// FIXME: https://github.com/rust-lang/rust-analyzer/issues/15253
|
||||||
|
"exactOptionalPropertyTypes": false
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", ".vscode-test"],
|
||||||
|
"include": ["client"]
|
||||||
|
}
|
2476
rust/kcl-language-server/yarn.lock
Normal file
2476
rust/kcl-language-server/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@ clap = { version = "4.5.27", default-features = false, optional = true, features
|
|||||||
"derive",
|
"derive",
|
||||||
] }
|
] }
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.8.0"
|
||||||
dashmap = "6.1.0"
|
dashmap = {workspace = true}
|
||||||
dhat = { version = "0.3", optional = true }
|
dhat = { version = "0.3", optional = true }
|
||||||
fnv = "1.0.7"
|
fnv = "1.0.7"
|
||||||
form_urlencoded = "1.2.1"
|
form_urlencoded = "1.2.1"
|
||||||
@ -35,7 +35,7 @@ itertools = "0.13.0"
|
|||||||
kcl-derive-docs = { version = "0.1.44", path = "../kcl-derive-docs" }
|
kcl-derive-docs = { version = "0.1.44", path = "../kcl-derive-docs" }
|
||||||
kittycad = { workspace = true }
|
kittycad = { workspace = true }
|
||||||
kittycad-modeling-cmds = { workspace = true }
|
kittycad-modeling-cmds = { workspace = true }
|
||||||
lazy_static = "1.5.0"
|
lazy_static = { workspace = true }
|
||||||
measurements = "0.11.0"
|
measurements = "0.11.0"
|
||||||
miette = { workspace = true }
|
miette = { workspace = true }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
@ -74,7 +74,7 @@ uuid = { workspace = true, features = ["v4", "js", "serde"] }
|
|||||||
validator = { version = "0.20.0", features = ["derive"] }
|
validator = { version = "0.20.0", features = ["derive"] }
|
||||||
web-time = "1.1"
|
web-time = "1.1"
|
||||||
winnow = "=0.6.24"
|
winnow = "=0.6.24"
|
||||||
zip = { version = "2.2.2", default-features = false }
|
zip = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
js-sys = { version = "0.3.72" }
|
js-sys = { version = "0.3.72" }
|
||||||
|
@ -23,6 +23,4 @@ We've built a lot of tooling to make contributing to KCL easier. If you are inte
|
|||||||
|
|
||||||
If you bump the version of kcl-lib and push it to crates, be sure to update the repos we own that use it as well. These are:
|
If you bump the version of kcl-lib and push it to crates, be sure to update the repos we own that use it as well. These are:
|
||||||
|
|
||||||
- [kcl.py](https://github.com/kittycad/kcl.py)
|
|
||||||
- [kcl-lsp](https://github.com/kittycad/kcl-lsp)
|
|
||||||
- [cli](https://github.com/kittycad/cli)
|
- [cli](https://github.com/kittycad/cli)
|
||||||
|
@ -205,8 +205,8 @@ impl EngineConnection {
|
|||||||
pub async fn new(ws: reqwest::Upgraded) -> Result<EngineConnection> {
|
pub async fn new(ws: reqwest::Upgraded) -> Result<EngineConnection> {
|
||||||
let wsconfig = tokio_tungstenite::tungstenite::protocol::WebSocketConfig {
|
let wsconfig = tokio_tungstenite::tungstenite::protocol::WebSocketConfig {
|
||||||
// 4294967296 bytes, which is around 4.2 GB.
|
// 4294967296 bytes, which is around 4.2 GB.
|
||||||
max_message_size: Some(0x100000000),
|
max_message_size: Some(usize::MAX),
|
||||||
max_frame_size: Some(0x100000000),
|
max_frame_size: Some(usize::MAX),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,3 +24,6 @@ serde = { workspace = true }
|
|||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
uuid = { workspace = true, features = ["v4"] }
|
uuid = { workspace = true, features = ["v4"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@ -37,6 +37,6 @@
|
|||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": ["src", "e2e", "packages", "*.ts", "rust"],
|
"include": ["src", "e2e", "packages", "*.ts", "rust"],
|
||||||
"exclude": ["node_modules", "./*.grammar", "vite.config.ts"],
|
"exclude": ["node_modules", "./*.grammar", "vite.config.ts", ".vscode-test"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ const config = defineConfig({
|
|||||||
coverage: {
|
coverage: {
|
||||||
provider: 'istanbul', // or 'v8'
|
provider: 'istanbul', // or 'v8'
|
||||||
},
|
},
|
||||||
exclude: [...configDefaults.exclude, '**/e2e/**/*'],
|
exclude: [...configDefaults.exclude, '**/e2e/**/*', 'rust'],
|
||||||
deps: {
|
deps: {
|
||||||
optimizer: {
|
optimizer: {
|
||||||
web: {
|
web: {
|
||||||
|
Reference in New Issue
Block a user