Compare commits
95 Commits
sketch-on-
...
callbacks-
Author | SHA1 | Date | |
---|---|---|---|
cf068f325e | |||
d60d2292f3 | |||
7d564cc2ac | |||
05a84213dd | |||
a85c1a9375 | |||
5701616f3e | |||
846acaba2f | |||
0a524d42f6 | |||
fe28527ef9 | |||
0e8d0083c4 | |||
4f4167b247 | |||
fbc2e9d02c | |||
33b15e818b | |||
6cebb84ae0 | |||
85403e47e4 | |||
0dfee64e3b | |||
6370d45f94 | |||
fb3e922180 | |||
1257ec0327 | |||
08e9fe2e52 | |||
7cec1d45fe | |||
93710bc8f2 | |||
87e7e9447f | |||
8be113d284 | |||
7cfc927d5c | |||
c0f04d5f86 | |||
3dbc701f26 | |||
16e7ae38e3 | |||
24c7260327 | |||
72cfc4a471 | |||
2d128ed32e | |||
cd6749ba02 | |||
7243405e1b | |||
c8da057ec2 | |||
220fe5b2b8 | |||
4e6429de49 | |||
5391a65b18 | |||
592628917a | |||
4c6e8633f7 | |||
c5150468a2 | |||
39126dbff1 | |||
f86a69f12a | |||
de354ee5d3 | |||
dfef7338ee | |||
ee08948f54 | |||
832f6b65e2 | |||
68efd77c5d | |||
8f138109dd | |||
8972f53256 | |||
0c5b13ade5 | |||
446f92a53a | |||
2256e3bc09 | |||
9e2876edc6 | |||
a138af1ec8 | |||
684c585a48 | |||
500be20649 | |||
5fbbe2fa8c | |||
5f5ecc5afe | |||
3dafc31cad | |||
9c230bc678 | |||
1fad6966b6 | |||
c7efb4c006 | |||
68fd921a64 | |||
a20e710e8f | |||
9daf2d7794 | |||
f86473d13b | |||
6fccc68c18 | |||
ade66d0876 | |||
b5f3a067ee | |||
bb9d24f821 | |||
bd3cd97d74 | |||
1b5839a7f8 | |||
a9e480f0ed | |||
63fa04608c | |||
0d4d7fa751 | |||
68cdb68231 | |||
053bdffc45 | |||
9ffc08b84a | |||
47e8d3f4fc | |||
53db421d97 | |||
289ed291c4 | |||
39ceb83840 | |||
af449ff6ca | |||
e1bf55cc4a | |||
8fe2d33063 | |||
d7e36eed24 | |||
6123ed6a82 | |||
da6cd5cf9f | |||
ad5bfa1a29 | |||
6358bdd7cd | |||
d6fe414b2e | |||
25e7e8cb89 | |||
a175870453 | |||
24516cdb2d | |||
496398de52 |
4
.github/workflows/cargo-bench.yml
vendored
@ -38,5 +38,7 @@ jobs:
|
||||
- name: Benchmark kcl library
|
||||
shell: bash
|
||||
run: |-
|
||||
cd src/wasm-lib/kcl; cargo bench -- iai
|
||||
cd src/wasm-lib/kcl; cargo bench --all-features -- iai
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
|
||||
|
13
.github/workflows/ci.yml
vendored
@ -138,6 +138,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
if: github.event_name == 'schedule'
|
||||
|
||||
- name: Copy updated .json files
|
||||
if: github.event_name == 'schedule'
|
||||
@ -238,12 +239,8 @@ jobs:
|
||||
shell: cmd
|
||||
|
||||
- name: Build the app (debug)
|
||||
uses: tauri-apps/tauri-action@v0
|
||||
if: ${{ env.BUILD_RELEASE == 'false' }}
|
||||
with:
|
||||
includeRelease: false
|
||||
includeDebug: true
|
||||
args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
|
||||
run: "yarn tauri build --debug ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
|
||||
|
||||
- name: Build for Mac TestFlight (nightly)
|
||||
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
|
||||
@ -336,7 +333,6 @@ jobs:
|
||||
# specific and we want to overwrite it with the this new build after and
|
||||
# not upload the apple store build to the public bucket
|
||||
- name: Build the app (release) and sign
|
||||
uses: tauri-apps/tauri-action@v0
|
||||
if: ${{ env.BUILD_RELEASE == 'true' }}
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
@ -348,8 +344,7 @@ jobs:
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
|
||||
with:
|
||||
args: "${{ env.TAURI_CONF_ARGS }} ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
|
||||
run: "yarn tauri build ${{ env.TAURI_CONF_ARGS }} ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
@ -383,6 +378,8 @@ jobs:
|
||||
TS_NODE_COMPILER_OPTIONS: '{"module": "commonjs"}'
|
||||
|
||||
publish-apps-release:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
|
||||
needs: [check-format, check-types, check-typos, build-test-web, prepare-json-files, build-test-apps]
|
||||
|
52
.github/workflows/playwright.yml
vendored
@ -38,6 +38,8 @@ jobs:
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
needs: check-rust-changes
|
||||
steps:
|
||||
- name: Tune GitHub-hosted runner network
|
||||
uses: smorimoto/tune-github-hosted-runner-network@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
@ -90,11 +92,17 @@ jobs:
|
||||
- name: build web
|
||||
run: yarn build:local
|
||||
- name: Run ubuntu/chrome snapshots
|
||||
run: yarn playwright test --project="Google Chrome" --update-snapshots e2e/playwright/snapshot-tests.spec.ts
|
||||
continue-on-error: true
|
||||
run: |
|
||||
yarn playwright test --project="Google Chrome" --update-snapshots e2e/playwright/snapshot-tests.spec.ts
|
||||
env:
|
||||
CI: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||
- name: Clean up test-results
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
run: rm -r test-results
|
||||
- name: check for changes
|
||||
id: git-check
|
||||
run: |
|
||||
@ -121,7 +129,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: steps.git-check.outputs.modified == 'true'
|
||||
with:
|
||||
name: playwright-report
|
||||
name: playwright-report-ubuntu-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
# if have previous run results, use them
|
||||
@ -129,16 +137,18 @@ jobs:
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: test-results
|
||||
name: test-results-ubuntu-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run ubuntu/chrome flow retry failures
|
||||
id: retry
|
||||
if: always()
|
||||
run: |
|
||||
ls -1 "test-results"
|
||||
if [[ $(ls -1 "test-results" | wc -l) == "0" ]];
|
||||
then echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
else echo "retried=true" >> $GITHUB_OUTPUT;
|
||||
if [[ -d "test-results" ]];
|
||||
then if [[ $(ls -1 "test-results" | wc -l) != "0" ]];
|
||||
then echo "retried=true" >> $GITHUB_OUTPUT;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
fi;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
fi;
|
||||
yarn playwright test --project="Google Chrome" --last-failed e2e/playwright/flow-tests.spec.ts
|
||||
env:
|
||||
@ -153,23 +163,25 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
name: test-results-ubuntu-${{ github.sha }}
|
||||
path: test-results/
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
name: playwright-report-ubuntu-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
||||
playwright-macos:
|
||||
timeout-minutes: 60
|
||||
runs-on: macos-14
|
||||
runs-on: macos-14-large
|
||||
needs: check-rust-changes
|
||||
steps:
|
||||
- name: Tune GitHub-hosted runner network
|
||||
uses: smorimoto/tune-github-hosted-runner-network@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
@ -227,23 +239,25 @@ jobs:
|
||||
if: ${{ always() }}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: test-results
|
||||
name: test-results-macos-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run macos/safari flow retry failures
|
||||
id: retry
|
||||
continue-on-error: true
|
||||
if: ${{ success() }}
|
||||
if: always()
|
||||
run: |
|
||||
if [ -d "test-results" ];
|
||||
then echo "retried=true" >> $GITHUB_OUTPUT;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT;
|
||||
if [[ -d "test-results" ]];
|
||||
then if [[ $(ls -1 "test-results" | wc -l) != "0" ]];
|
||||
then echo "retried=true" >> $GITHUB_OUTPUT;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
fi;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
fi;
|
||||
yarn playwright test --project="webkit" --last-failed e2e/playwright/flow-tests.spec.ts
|
||||
env:
|
||||
CI: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
- name: Run macos/safari flow
|
||||
if: ${{ steps.retry.outputs.retried != 'true' }}
|
||||
if: steps.retry.outputs.retried == 'false'
|
||||
# webkit doesn't work on Ubuntu because of the same reason tauri doesn't (webRTC issues)
|
||||
# TODO remove this and the matrix and run all tests on ubuntu when this is fixed
|
||||
run: yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts
|
||||
@ -253,14 +267,14 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: test-results
|
||||
name: test-results-macos-${{ github.sha }}
|
||||
path: test-results/
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: playwright-report
|
||||
name: playwright-report-macos-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
2
.gitignore
vendored
@ -56,3 +56,5 @@ src-tauri/gen
|
||||
|
||||
src/wasm-lib/grackle/stdlib_cube_partial.json
|
||||
Mac_App_Distribution.provisionprofile
|
||||
|
||||
*.tsbuildinfo
|
||||
|
@ -1,5 +1,6 @@
|
||||
# Ignore artifacts:
|
||||
build
|
||||
dist
|
||||
coverage
|
||||
|
||||
# Ignore Rust projects:
|
||||
@ -9,5 +10,6 @@ src/wasm-lib/pkg
|
||||
src/wasm-lib/kcl/bindings
|
||||
e2e/playwright/export-snapshots
|
||||
|
||||
|
||||
# XState generated files
|
||||
src/machines/**.typegen.ts
|
||||
|
7
.vscode/settings.json
vendored
@ -1,7 +0,0 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"geos"
|
||||
],
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
}
|
26
README.md
@ -124,36 +124,20 @@ Before you submit a contribution PR to this repo, please ensure that:
|
||||
|
||||
## Release a new version
|
||||
|
||||
1. Bump the versions in the .json files by creating a `Cut release v{x}.{y}.{z}` PR, committing the changes from
|
||||
1. Bump the versions by running `./make-realease.sh` while on a fresh pull of main
|
||||
|
||||
```bash
|
||||
VERSION=x.y.z yarn run bump-jsons
|
||||
```
|
||||
|
||||
Alternatively you can try the experimental `make-release.sh` bash script that will create the branch with the updated json files for you.
|
||||
That will create the branch with the updated json files for you.
|
||||
run `./make-release.sh` for a patch update
|
||||
run `./make-release.sh "minor"` for minor
|
||||
run `./make-release.sh "major"` for major
|
||||
|
||||
The PR may serve as a place to discuss the human-readable changelog and extra QA. A quick way of getting PR's merged since the last bump is to [use this PR filter](https://github.com/KittyCAD/modeling-app/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Amerged+), open up the browser console and paste in the following
|
||||
After it runs you should just need to push the push the branch and open a PR (it will suggest a changelog for you too, delete any that are not user facing)
|
||||
|
||||
```typescript
|
||||
console.log(
|
||||
'- ' +
|
||||
Array.from(
|
||||
document.querySelectorAll('[data-hovercard-type="pull_request"]')
|
||||
).map((a) => `[${a.innerText}](${a.href})`).join(`
|
||||
- `)
|
||||
)
|
||||
```
|
||||
|
||||
grab the md list and delete any that are older than the last bump
|
||||
The PR may serve as a place to discuss the human-readable changelog and extra QA.
|
||||
|
||||
2. Merge the PR
|
||||
|
||||
3. Create a new release and tag pointing to the bump version commit using semantic versioning `v{x}.{y}.{z}`
|
||||
|
||||
4. A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, uploading artifacts to the release
|
||||
3. Profit (A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions if the PR was correctly named)
|
||||
|
||||
## Fuzzing the parser
|
||||
|
||||
|
@ -17,11 +17,8 @@ angleToMatchLengthX(segment_name: TagIdentifier, to: number, sketch_group: Sketc
|
||||
```js
|
||||
const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([2, 5], %, 'seg01')
|
||||
|> angledLineToX([
|
||||
-angleToMatchLengthX('seg01', 7, %),
|
||||
10
|
||||
], %)
|
||||
|> line([2, 5], %, $seg01)
|
||||
|> angledLineToX([-angleToMatchLengthX(seg01, 7, %), 10], %)
|
||||
|> close(%)
|
||||
|
||||
const extrusion = extrude(5, sketch001)
|
||||
@ -124,6 +121,9 @@ const extrusion = extrude(5, sketch001)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -285,6 +285,9 @@ const extrusion = extrude(5, sketch001)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -17,9 +17,9 @@ angleToMatchLengthY(segment_name: TagIdentifier, to: number, sketch_group: Sketc
|
||||
```js
|
||||
const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([1, 2], %, 'seg01')
|
||||
|> line([1, 2], %, $seg01)
|
||||
|> angledLine({
|
||||
angle: angleToMatchLengthY('seg01', 15, %),
|
||||
angle: angleToMatchLengthY(seg01, 15, %),
|
||||
length: 5
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
@ -125,6 +125,9 @@ const extrusion = extrude(5, sketch001)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -286,6 +289,9 @@ const extrusion = extrude(5, sketch001)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -126,6 +126,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -287,6 +290,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -476,6 +482,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -637,6 +646,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -17,10 +17,10 @@ angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup, tag?: TagDe
|
||||
```js
|
||||
const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLineOfXLength({ angle: 45, length: 10 }, %, "edge1")
|
||||
|> angledLineOfXLength({ angle: -15, length: 20 }, %, "edge2")
|
||||
|> angledLineOfXLength({ angle: 45, length: 10 }, %, $edge1)
|
||||
|> angledLineOfXLength({ angle: -15, length: 20 }, %, $edge2)
|
||||
|> line([0, -5], %)
|
||||
|> close(%, "edge3")
|
||||
|> close(%, $edge3)
|
||||
|
||||
const extrusion = extrude(10, sketch001)
|
||||
```
|
||||
@ -125,6 +125,9 @@ const extrusion = extrude(10, sketch001)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -286,6 +289,9 @@ const extrusion = extrude(10, sketch001)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -475,6 +481,9 @@ const extrusion = extrude(10, sketch001)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -636,6 +645,9 @@ const extrusion = extrude(10, sketch001)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -18,7 +18,7 @@ angledLineThatIntersects(data: AngledLineThatIntersectsData, sketch_group: Sketc
|
||||
const exampleSketch = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([5, 10], %)
|
||||
|> lineTo([-10, 10], %, "lineToIntersect")
|
||||
|> lineTo([-10, 10], %, $lineToIntersect)
|
||||
|> lineTo([0, 20], %)
|
||||
|> angledLineThatIntersects({
|
||||
angle: 80,
|
||||
@ -133,6 +133,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -294,6 +297,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -483,6 +489,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -644,6 +653,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -124,6 +124,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -285,6 +288,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -474,6 +480,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -635,6 +644,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -124,6 +124,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -285,6 +288,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -474,6 +480,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -635,6 +644,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -135,6 +135,9 @@ const exampleSketch = startSketchOn('XZ')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -296,6 +299,9 @@ const exampleSketch = startSketchOn('XZ')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -485,6 +491,9 @@ const exampleSketch = startSketchOn('XZ')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -646,6 +655,9 @@ const exampleSketch = startSketchOn('XZ')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -130,6 +130,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -291,6 +294,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -480,6 +486,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -641,6 +650,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -22,19 +22,19 @@ const chamferLength = 2
|
||||
|
||||
const mountingPlateSketch = startSketchOn("XY")
|
||||
|> startProfileAt([-width / 2, -length / 2], %)
|
||||
|> lineTo([width / 2, -length / 2], %, 'edge1')
|
||||
|> lineTo([width / 2, length / 2], %, 'edge2')
|
||||
|> lineTo([-width / 2, length / 2], %, 'edge3')
|
||||
|> close(%, 'edge4')
|
||||
|> lineTo([width / 2, -length / 2], %, $edge1)
|
||||
|> lineTo([width / 2, length / 2], %, $edge2)
|
||||
|> lineTo([-width / 2, length / 2], %, $edge3)
|
||||
|> close(%, $edge4)
|
||||
|
||||
const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
|> chamfer({
|
||||
length: chamferLength,
|
||||
tags: [
|
||||
getNextAdjacentEdge('edge1', %),
|
||||
getNextAdjacentEdge('edge2', %),
|
||||
getNextAdjacentEdge('edge3', %),
|
||||
getNextAdjacentEdge('edge4', %)
|
||||
getNextAdjacentEdge(edge1, %),
|
||||
getNextAdjacentEdge(edge2, %),
|
||||
getNextAdjacentEdge(edge3, %),
|
||||
getNextAdjacentEdge(edge4, %)
|
||||
]
|
||||
}, %)
|
||||
```
|
||||
@ -225,6 +225,9 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -532,6 +535,9 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -9,7 +9,7 @@ Sketch a circle.
|
||||
|
||||
|
||||
```js
|
||||
circle(center: [number], radius: number, tag?: TagDeclarator, sketch_surface_or_group: SketchSurfaceOrGroup) -> SketchGroup
|
||||
circle(center: [number], radius: number, sketch_surface_or_group: SketchSurfaceOrGroup, tag?: TagDeclarator) -> SketchGroup
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -41,14 +41,6 @@ const example = extrude(5, exampleSketch)
|
||||
|
||||
* `center`: `[number]` (REQUIRED)
|
||||
* `radius`: `number` (REQUIRED)
|
||||
* `tag`: `TagDeclarator` (OPTIONAL)
|
||||
```js
|
||||
{
|
||||
end: number,
|
||||
start: number,
|
||||
value: string,
|
||||
}
|
||||
```
|
||||
* `sketch_surface_or_group`: `SketchSurfaceOrGroup` - A sketch surface or a sketch group. (REQUIRED)
|
||||
```js
|
||||
{
|
||||
@ -131,6 +123,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -418,6 +413,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -510,6 +508,14 @@ const example = extrude(5, exampleSketch)
|
||||
}],
|
||||
}
|
||||
```
|
||||
* `tag`: `TagDeclarator` (OPTIONAL)
|
||||
```js
|
||||
{
|
||||
end: number,
|
||||
start: number,
|
||||
value: string,
|
||||
}
|
||||
```
|
||||
|
||||
### Returns
|
||||
|
||||
@ -599,6 +605,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -760,6 +769,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -125,6 +125,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -286,6 +289,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -475,6 +481,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -636,6 +645,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -150,6 +150,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -311,6 +314,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
type: "sketchGroup",
|
||||
// The paths in the sketch group.
|
||||
@ -580,6 +586,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -22,19 +22,19 @@ const filletRadius = 2
|
||||
|
||||
const mountingPlateSketch = startSketchOn("XY")
|
||||
|> startProfileAt([-width / 2, -length / 2], %)
|
||||
|> lineTo([width / 2, -length / 2], %, 'edge1')
|
||||
|> lineTo([width / 2, length / 2], %, 'edge2')
|
||||
|> lineTo([-width / 2, length / 2], %, 'edge3')
|
||||
|> close(%, 'edge4')
|
||||
|> lineTo([width / 2, -length / 2], %, $edge1)
|
||||
|> lineTo([width / 2, length / 2], %, $edge2)
|
||||
|> lineTo([-width / 2, length / 2], %, $edge3)
|
||||
|> close(%, $edge4)
|
||||
|
||||
const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
|> fillet({
|
||||
radius: filletRadius,
|
||||
tags: [
|
||||
getNextAdjacentEdge('edge1', %),
|
||||
getNextAdjacentEdge('edge2', %),
|
||||
getNextAdjacentEdge('edge3', %),
|
||||
getNextAdjacentEdge('edge4', %)
|
||||
getNextAdjacentEdge(edge1, %),
|
||||
getNextAdjacentEdge(edge2, %),
|
||||
getNextAdjacentEdge(edge3, %),
|
||||
getNextAdjacentEdge(edge4, %)
|
||||
]
|
||||
}, %)
|
||||
```
|
||||
@ -225,6 +225,9 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -524,6 +527,9 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -17,7 +17,7 @@ getEdge(tag: TagIdentifier, extrude_group: ExtrudeGroup) -> Uuid
|
||||
```js
|
||||
const box = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 10], %, 'revolveAxis')
|
||||
|> line([0, 10], %, $revolveAxis)
|
||||
|> line([10, 0], %)
|
||||
|> line([0, -10], %)
|
||||
|> close(%)
|
||||
@ -30,7 +30,7 @@ const revolution = startSketchOn(box, "revolveAxis")
|
||||
|> line([0, -10], %)
|
||||
|> close(%)
|
||||
|> revolve({
|
||||
axis: getEdge('revolveAxis', box),
|
||||
axis: getEdge(revolveAxis, box),
|
||||
angle: 90
|
||||
}, %)
|
||||
```
|
||||
@ -215,6 +215,9 @@ const revolution = startSketchOn(box, "revolveAxis")
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -21,15 +21,13 @@ const exampleSketch = startSketchOn('XZ')
|
||||
|> angledLine({ angle: 60, length: 10 }, %)
|
||||
|> angledLine({ angle: 120, length: 10 }, %)
|
||||
|> line([-10, 0], %)
|
||||
|> angledLine({ angle: 240, length: 10 }, %, 'referenceEdge')
|
||||
|> angledLine({ angle: 240, length: 10 }, %, $referenceEdge)
|
||||
|> close(%)
|
||||
|
||||
const example = extrude(5, exampleSketch)
|
||||
|> fillet({
|
||||
radius: 3,
|
||||
tags: [
|
||||
getNextAdjacentEdge("referenceEdge", %)
|
||||
]
|
||||
tags: [getNextAdjacentEdge(referenceEdge, %)]
|
||||
}, %)
|
||||
```
|
||||
|
||||
@ -213,6 +211,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -21,13 +21,13 @@ const exampleSketch = startSketchOn('XZ')
|
||||
|> angledLine({ angle: 60, length: 10 }, %)
|
||||
|> angledLine({ angle: 120, length: 10 }, %)
|
||||
|> line([-10, 0], %)
|
||||
|> angledLine({ angle: 240, length: 10 }, %, 'referenceEdge')
|
||||
|> angledLine({ angle: 240, length: 10 }, %, $referenceEdge)
|
||||
|> close(%)
|
||||
|
||||
const example = extrude(5, exampleSketch)
|
||||
|> fillet({
|
||||
radius: 3,
|
||||
tags: [getOppositeEdge("referenceEdge", %)]
|
||||
tags: [getOppositeEdge(referenceEdge, %)]
|
||||
}, %)
|
||||
```
|
||||
|
||||
@ -211,6 +211,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -21,14 +21,14 @@ const exampleSketch = startSketchOn('XZ')
|
||||
|> angledLine({ angle: 60, length: 10 }, %)
|
||||
|> angledLine({ angle: 120, length: 10 }, %)
|
||||
|> line([-10, 0], %)
|
||||
|> angledLine({ angle: 240, length: 10 }, %, 'referenceEdge')
|
||||
|> angledLine({ angle: 240, length: 10 }, %, $referenceEdge)
|
||||
|> close(%)
|
||||
|
||||
const example = extrude(5, exampleSketch)
|
||||
|> fillet({
|
||||
radius: 3,
|
||||
tags: [
|
||||
getPreviousAdjacentEdge("referenceEdge", %)
|
||||
getPreviousAdjacentEdge(referenceEdge, %)
|
||||
]
|
||||
}, %)
|
||||
```
|
||||
@ -213,6 +213,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -213,6 +213,9 @@ const part001 = startSketchOn('XY')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -512,6 +515,9 @@ const part001 = startSketchOn('XY')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -136,6 +136,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -297,6 +300,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
type: "sketchGroup",
|
||||
// The paths in the sketch group.
|
||||
@ -479,6 +485,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -640,6 +649,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -821,6 +833,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -982,6 +997,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -55,6 +55,7 @@ layout: manual
|
||||
* [`patternCircular3d`](kcl/patternCircular3d)
|
||||
* [`patternLinear2d`](kcl/patternLinear2d)
|
||||
* [`patternLinear3d`](kcl/patternLinear3d)
|
||||
* [`patternTransform`](kcl/patternTransform)
|
||||
* [`pi`](kcl/pi)
|
||||
* [`pow`](kcl/pow)
|
||||
* [`profileStart`](kcl/profileStart)
|
||||
|
@ -116,6 +116,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -277,6 +280,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -116,6 +116,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -277,6 +280,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -129,6 +129,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -290,6 +293,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -479,6 +485,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -640,6 +649,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -116,6 +116,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -277,6 +280,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -466,6 +472,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -627,6 +636,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -134,6 +134,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -295,6 +298,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
type: "sketchGroup",
|
||||
// The paths in the sketch group.
|
||||
|
@ -217,6 +217,9 @@ const example = extrude(-5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -127,6 +127,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -288,6 +291,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
type: "sketchGroup",
|
||||
// The paths in the sketch group.
|
||||
|
@ -215,6 +215,9 @@ const example = extrude(1, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
359
docs/kcl/patternTransform.md
Normal file
@ -17,9 +17,9 @@ profileStart(sketch_group: SketchGroup) -> [number]
|
||||
```js
|
||||
const sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([5, 2], %)
|
||||
|> angledLine({ angle: 120, length: 50 }, %, 'seg01')
|
||||
|> angledLine({ angle: 120, length: 50 }, %, $seg01)
|
||||
|> angledLine({
|
||||
angle: segAng('seg01', %) + 120,
|
||||
angle: segAng(seg01, %) + 120,
|
||||
length: 50
|
||||
}, %)
|
||||
|> lineTo(profileStart(%), %)
|
||||
@ -117,6 +117,9 @@ const sketch001 = startSketchOn('XY')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -278,6 +281,9 @@ const sketch001 = startSketchOn('XY')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -112,6 +112,9 @@ const sketch001 = startSketchOn('XY')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -273,6 +276,9 @@ const sketch001 = startSketchOn('XY')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -111,6 +111,9 @@ const sketch001 = startSketchOn('XY')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -272,6 +275,9 @@ const sketch001 = startSketchOn('XY')
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -99,7 +99,7 @@ const box = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %, 'revolveAxis')
|
||||
|> line([0, -20], %, $revolveAxis)
|
||||
|> close(%)
|
||||
|> extrude(20, %)
|
||||
|
||||
@ -107,7 +107,7 @@ const sketch001 = startSketchOn(box, "END")
|
||||
|> circle([10, 10], 4, %)
|
||||
|> revolve({
|
||||
angle: 90,
|
||||
axis: getOppositeEdge('revolveAxis', box)
|
||||
axis: getOppositeEdge(revolveAxis, box)
|
||||
}, %)
|
||||
```
|
||||
|
||||
@ -247,6 +247,9 @@ uuid |
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -408,6 +411,9 @@ uuid |
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -673,6 +679,9 @@ uuid |
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -18,11 +18,11 @@ segAng(segment_name: TagIdentifier, sketch_group: SketchGroup) -> number
|
||||
const exampleSketch = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([5, 10], %, 'seg01')
|
||||
|> line([5, 10], %, $seg01)
|
||||
|> line([-10, 0], %)
|
||||
|> angledLine([segAng('seg01', %), 10], %)
|
||||
|> angledLine([segAng(seg01, %), 10], %)
|
||||
|> line([-10, 0], %)
|
||||
|> angledLine([segAng('seg01', %), -15], %)
|
||||
|> angledLine([segAng(seg01, %), -15], %)
|
||||
|> close(%)
|
||||
|
||||
const example = extrude(4, exampleSketch)
|
||||
@ -124,6 +124,9 @@ const example = extrude(4, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -285,6 +288,9 @@ const example = extrude(4, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -17,9 +17,9 @@ segEndX(segment_name: TagIdentifier, sketch_group: SketchGroup) -> number
|
||||
```js
|
||||
const exampleSketch = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([20, 0], %, "thing")
|
||||
|> line([20, 0], %, $thing)
|
||||
|> line([0, 5], %)
|
||||
|> line([segEndX("thing", %), 0], %)
|
||||
|> line([segEndX(thing, %), 0], %)
|
||||
|> line([-20, 10], %)
|
||||
|> close(%)
|
||||
|
||||
@ -122,6 +122,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -283,6 +286,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -18,9 +18,9 @@ segEndY(segment_name: TagIdentifier, sketch_group: SketchGroup) -> number
|
||||
const exampleSketch = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 3], %, "thing")
|
||||
|> line([0, 3], %, $thing)
|
||||
|> line([-10, 0], %)
|
||||
|> line([0, segEndY("thing", %)], %)
|
||||
|> line([0, segEndY(thing, %)], %)
|
||||
|> line([-10, 0], %)
|
||||
|> close(%)
|
||||
|
||||
@ -123,6 +123,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -284,6 +287,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -17,12 +17,9 @@ segLen(segment_name: TagIdentifier, sketch_group: SketchGroup) -> number
|
||||
```js
|
||||
const exampleSketch = startSketchOn("XZ")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle: 60, length: 10 }, %, "thing")
|
||||
|> angledLine({ angle: 60, length: 10 }, %, $thing)
|
||||
|> tangentialArc({ offset: -120, radius: 5 }, %)
|
||||
|> angledLine({
|
||||
angle: -60,
|
||||
length: segLen("thing", %)
|
||||
}, %)
|
||||
|> angledLine({ angle: -60, length: segLen(thing, %) }, %)
|
||||
|> close(%)
|
||||
|
||||
const example = extrude(5, exampleSketch)
|
||||
@ -124,6 +121,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -285,6 +285,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -213,6 +213,9 @@ shell({ faces: ['end'], thickness: 0.25 }, firstSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -512,6 +515,9 @@ shell({ faces: ['end'], thickness: 0.25 }, firstSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -195,6 +195,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -442,6 +445,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -603,6 +609,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -142,6 +142,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -303,6 +306,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -49,7 +49,7 @@ const example003 = extrude(5, exampleSketch003)
|
||||
const exampleSketch = startSketchOn("XY")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([0, 10], %, 'sketchingFace')
|
||||
|> line([0, 10], %, $sketchingFace)
|
||||
|> line([-10, 0], %)
|
||||
|> close(%)
|
||||
|
||||
@ -60,7 +60,7 @@ const exampleSketch002 = startSketchOn(example, 'sketchingFace')
|
||||
|> line([8, 0], %)
|
||||
|> line([0, 8], %)
|
||||
|> line([-8, 0], %)
|
||||
|> close(%, 'sketchingFace002')
|
||||
|> close(%, $sketchingFace002)
|
||||
|
||||
const example002 = extrude(10, exampleSketch002)
|
||||
|
||||
@ -264,6 +264,9 @@ const a1 = startSketchOn({
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -539,6 +542,9 @@ const a1 = startSketchOn({
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
9977
docs/kcl/std.json
@ -125,6 +125,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -286,6 +289,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -475,6 +481,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -636,6 +645,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -116,6 +116,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -277,6 +280,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -466,6 +472,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -627,6 +636,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -74,6 +74,107 @@ You can nest expressions in parenthesis as well:
|
||||
let myMathExpression = 3 + (1 * 2 / (3 - 7))
|
||||
```
|
||||
|
||||
Please if you find any issues using any of the above expressions or syntax
|
||||
## Tags
|
||||
|
||||
Tags are used to give a name (tag) to a specific path.
|
||||
|
||||
### Tag Declaration
|
||||
|
||||
The syntax for declaring a tag is `$myTag` you would use it in the following
|
||||
way:
|
||||
|
||||
```
|
||||
startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001, %) - 90,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001, %),
|
||||
-segLen(rectangleSegmentA001, %)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
```
|
||||
|
||||
### Tag Identifier
|
||||
|
||||
As per the example above you can use the tag identifier to get a reference to the
|
||||
tagged object. The syntax for this is `myTag`.
|
||||
|
||||
In the example above we use the tag identifier to get the angle of the segment
|
||||
`segAng(rectangleSegmentA001, %)`.
|
||||
|
||||
|
||||
### Tag Scope
|
||||
|
||||
Tags are scoped globally if in the root context meaning in this example you can
|
||||
use the tag `rectangleSegmentA001` in any function or expression in the file.
|
||||
|
||||
However if the code was written like this:
|
||||
|
||||
```
|
||||
fn rect = (origin) => {
|
||||
return startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001, %) - 90,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001, %),
|
||||
-segLen(rectangleSegmentA001, %)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
}
|
||||
|
||||
rect([0, 0])
|
||||
rect([20, 0])
|
||||
```
|
||||
|
||||
Those tags would only be available in the `rect` function and not globally.
|
||||
|
||||
However you likely want to use those tags somewhere outside the `rect` function.
|
||||
|
||||
Tags are accessible through the sketch group they are declared in.
|
||||
For example the following code works.
|
||||
|
||||
```
|
||||
fn rect = (origin) => {
|
||||
return startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001, %) - 90,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001, %),
|
||||
-segLen(rectangleSegmentA001, %)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
}
|
||||
|
||||
rect([0, 0])
|
||||
const myRect = rect([20, 0])
|
||||
|
||||
myRect
|
||||
|> extrude(10, %)
|
||||
|> fillet({radius: 0.5, tags: [myRect.tags.rectangleSegmentA001]}, %)
|
||||
```
|
||||
|
||||
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside
|
||||
the `rect` function. This is because the `rect` function is returning the
|
||||
sketch group that contains the tags.
|
||||
|
||||
|
||||
---
|
||||
|
||||
If you find any issues using any of the above expressions or syntax,
|
||||
please file an issue with the `ast` label on the [modeling-app
|
||||
repo](https://github.com/KittyCAD/modeling-app/issues/new).
|
||||
|
@ -119,6 +119,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -280,6 +283,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -469,6 +475,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -630,6 +639,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -119,6 +119,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -280,6 +283,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -469,6 +475,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -630,6 +639,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -117,6 +117,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -278,6 +281,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -467,6 +473,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -628,6 +637,9 @@ const example = extrude(10, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -115,6 +115,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -276,6 +279,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -465,6 +471,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
@ -626,6 +635,9 @@ const example = extrude(5, exampleSketch)
|
||||
},
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
// Tag identifiers that have been declared in this sketch group.
|
||||
tags: {
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
|
@ -91,8 +91,9 @@ const part001 = startSketchOn('-XZ')
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.waitForCmdReceive('extrude')
|
||||
@ -330,7 +331,7 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// wait for execution done
|
||||
@ -386,8 +387,8 @@ test('Draft segments should look right', async ({ page, context }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(
|
||||
@ -443,7 +444,7 @@ test('Draft rectangles should look right', async ({ page, context }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.goto('/')
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
|
||||
@ -490,7 +491,7 @@ test.describe('Client side scene scale should match engine scale', () => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.goto('/')
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
|
||||
@ -589,7 +590,7 @@ test.describe('Client side scene scale should match engine scale', () => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.goto('/')
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
|
||||
@ -689,7 +690,7 @@ const part002 = startSketchOn(part001, 'seg01')
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
@ -739,7 +740,7 @@ test('Zoom to fit on load - solid 2d', async ({ page, context }) => {
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
@ -776,7 +777,7 @@ test('Zoom to fit on load - solid 3d', async ({ page, context }) => {
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
@ -795,3 +796,83 @@ test('Zoom to fit on load - solid 3d', async ({ page, context }) => {
|
||||
maxDiffPixels: 100,
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Grid visibility', () => {
|
||||
test('Grid turned off', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
const stream = page.getByTestId('stream')
|
||||
const mask = [
|
||||
page.locator('#app-header'),
|
||||
page.locator('#sidebar-top-ribbon'),
|
||||
page.locator('#sidebar-bottom-ribbon'),
|
||||
]
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
// wait for execution done
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(2)
|
||||
await u.closeDebugPanel()
|
||||
await u.closeKclCodePanel()
|
||||
// TODO: Find a way to truly know that the objects have finished
|
||||
// rendering, because an execution-done message is not sufficient.
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await expect(stream).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
mask,
|
||||
})
|
||||
})
|
||||
|
||||
test('Grid turned on', async ({ page }) => {
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
modeling: {
|
||||
...TEST_SETTINGS.modeling,
|
||||
showScaleGrid: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
const u = await getUtils(page)
|
||||
const stream = page.getByTestId('stream')
|
||||
const mask = [
|
||||
page.locator('#app-header'),
|
||||
page.locator('#sidebar-top-ribbon'),
|
||||
page.locator('#sidebar-bottom-ribbon'),
|
||||
]
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
// wait for execution done
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(2)
|
||||
await u.closeDebugPanel()
|
||||
await u.closeKclCodePanel()
|
||||
// TODO: Find a way to truly know that the objects have finished
|
||||
// rendering, because an execution-done message is not sufficient.
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await expect(stream).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
mask,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 29 KiB |
@ -45,8 +45,8 @@ async function clearCommandLogs(page: Page) {
|
||||
await page.getByTestId('clear-commands').click()
|
||||
}
|
||||
|
||||
async function expectCmdLog(page: Page, locatorStr: string) {
|
||||
await expect(page.locator(locatorStr).last()).toBeVisible()
|
||||
async function expectCmdLog(page: Page, locatorStr: string, timeout = 5000) {
|
||||
await expect(page.locator(locatorStr).last()).toBeVisible({ timeout })
|
||||
}
|
||||
|
||||
async function waitForDefaultPlanesToBeVisible(page: Page) {
|
||||
@ -207,6 +207,23 @@ export const getMovementUtils = (opts: any) => {
|
||||
return { toSU, click00r }
|
||||
}
|
||||
|
||||
async function waitForAuthAndLsp(page: Page) {
|
||||
const waitForLspPromise = page.waitForEvent('console', async (message) => {
|
||||
// it would be better to wait for a message that the kcl lsp has started by looking for the message message.text().includes('[lsp] [window/logMessage]')
|
||||
// but that doesn't seem to make it to the console for macos/safari :(
|
||||
if (message.text().includes('start kcl lsp')) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
await page.goto('/')
|
||||
await waitForPageLoad(page)
|
||||
|
||||
return waitForLspPromise
|
||||
}
|
||||
|
||||
export async function getUtils(page: Page) {
|
||||
// Chrome devtools protocol session only works in Chromium
|
||||
const browserType = page.context().browser()?.browserType().name()
|
||||
@ -214,7 +231,8 @@ export async function getUtils(page: Page) {
|
||||
browserType !== 'chromium' ? null : await page.context().newCDPSession(page)
|
||||
|
||||
return {
|
||||
waitForAuthSkipAppStart: () => waitForPageLoad(page),
|
||||
waitForAuthSkipAppStart: () => waitForAuthAndLsp(page),
|
||||
waitForPageLoad: () => waitForPageLoad(page),
|
||||
removeCurrentCode: () => removeCurrentCode(page),
|
||||
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
|
||||
updateCamPosition: async (xyz: [number, number, number]) => {
|
||||
@ -228,7 +246,8 @@ export async function getUtils(page: Page) {
|
||||
await fillInput('z', xyz[2])
|
||||
},
|
||||
clearCommandLogs: () => clearCommandLogs(page),
|
||||
expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr),
|
||||
expectCmdLog: (locatorStr: string, timeout = 5000) =>
|
||||
expectCmdLog(page, locatorStr, timeout),
|
||||
openKclCodePanel: () => openKclCodePanel(page),
|
||||
closeKclCodePanel: () => closeKclCodePanel(page),
|
||||
openDebugPanel: () => openDebugPanel(page),
|
||||
@ -300,11 +319,19 @@ export async function getUtils(page: Page) {
|
||||
(screenshot.width * coords.y * pixMultiplier +
|
||||
coords.x * pixMultiplier) *
|
||||
4 // rbga is 4 channels
|
||||
return Math.max(
|
||||
const maxDiff = Math.max(
|
||||
Math.abs(screenshot.data[index] - expected[0]),
|
||||
Math.abs(screenshot.data[index + 1] - expected[1]),
|
||||
Math.abs(screenshot.data[index + 2] - expected[2])
|
||||
)
|
||||
if (maxDiff > 4) {
|
||||
console.log(
|
||||
`Expected: ${expected} Actual: [${screenshot.data[index]}, ${
|
||||
screenshot.data[index + 1]
|
||||
}, ${screenshot.data[index + 2]}]`
|
||||
)
|
||||
}
|
||||
return maxDiff
|
||||
},
|
||||
doAndWaitForImageDiff: (fn: () => Promise<any>, diffCount = 200) =>
|
||||
new Promise(async (resolve) => {
|
||||
|
@ -1,3 +0,0 @@
|
||||
// comment
|
||||
|
||||
const hi = 5 + 4
|
83
package.json
@ -1,50 +1,43 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.22.6",
|
||||
"version": "0.23.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.16.0",
|
||||
"@codemirror/autocomplete": "^6.17.0",
|
||||
"@codemirror/commands": "^6.6.0",
|
||||
"@codemirror/language": "^6.10.2",
|
||||
"@codemirror/lint": "^6.8.1",
|
||||
"@codemirror/search": "^6.5.6",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@csstools/postcss-oklab-function": "^3.0.16",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.19",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "^0.0.67",
|
||||
"@lezer/javascript": "^1.4.9",
|
||||
"@open-rpc/client-js": "^1.8.1",
|
||||
"@kittycad/lib": "^0.0.69",
|
||||
"@react-hook/resize-observer": "^2.0.1",
|
||||
"@replit/codemirror-interact": "^6.3.1",
|
||||
"@tauri-apps/api": "2.0.0-beta.12",
|
||||
"@tauri-apps/plugin-dialog": "^2.0.0-beta.2",
|
||||
"@tauri-apps/plugin-fs": "^2.0.0-beta.3",
|
||||
"@tauri-apps/plugin-http": "^2.0.0-beta.2",
|
||||
"@tauri-apps/plugin-os": "^2.0.0-beta.3",
|
||||
"@tauri-apps/plugin-process": "^2.0.0-beta.2",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-beta.2",
|
||||
"@tauri-apps/plugin-updater": "^2.0.0-beta.3",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@tauri-apps/api": "^2.0.0-beta.14",
|
||||
"@tauri-apps/plugin-dialog": "^2.0.0-beta.6",
|
||||
"@tauri-apps/plugin-fs": "^2.0.0-beta.6",
|
||||
"@tauri-apps/plugin-http": "^2.0.0-beta.7",
|
||||
"@tauri-apps/plugin-os": "^2.0.0-beta.6",
|
||||
"@tauri-apps/plugin-process": "^2.0.0-beta.6",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-beta.7",
|
||||
"@tauri-apps/plugin-updater": "^2.0.0-beta.6",
|
||||
"@ts-stack/markdown": "^1.5.0",
|
||||
"@tweenjs/tween.js": "^23.1.1",
|
||||
"@types/node": "^18.19.31",
|
||||
"@types/react": "^18.3.2",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@uiw/react-codemirror": "^4.21.25",
|
||||
"@xstate/inspect": "^0.8.0",
|
||||
"@xstate/react": "^3.2.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"debounce-promise": "^3.1.2",
|
||||
"codemirror": "^6.0.1",
|
||||
"decamelize": "^6.0.0",
|
||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||
"formik": "^2.4.6",
|
||||
"fuse.js": "^7.0.0",
|
||||
"html2canvas-pro": "^1.4.3",
|
||||
"http-server": "^14.1.1",
|
||||
"html2canvas-pro": "^1.5.2",
|
||||
"json-rpc-2.0": "^1.6.0",
|
||||
"jszip": "^3.10.1",
|
||||
"node-fetch": "^3.3.2",
|
||||
"re-resizable": "^6.9.11",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -55,20 +48,15 @@
|
||||
"react-modal-promise": "^1.0.2",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"sketch-helpers": "^0.0.4",
|
||||
"swr": "^2.2.5",
|
||||
"three": "^0.164.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"three": "^0.166.1",
|
||||
"typescript": "^5.4.5",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"uuid": "^9.0.1",
|
||||
"vitest": "^1.6.0",
|
||||
"vscode-jsonrpc": "^8.2.1",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"wasm-pack": "^0.12.1",
|
||||
"vscode-uri": "^3.0.8",
|
||||
"web-vitals": "^3.5.2",
|
||||
"ws": "^8.17.0",
|
||||
"xstate": "^4.38.2",
|
||||
"zustand": "^4.5.2"
|
||||
"xstate": "^4.38.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
@ -85,11 +73,11 @@
|
||||
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e",
|
||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e",
|
||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
||||
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||
"build:wasm": "(cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||
"build:wasm": "(cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
|
||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
||||
@ -121,13 +109,16 @@
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@babel/preset-env": "^7.24.3",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@tauri-apps/cli": "^2.0.0-beta.13",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/debounce-promise": "^3.1.9",
|
||||
"@playwright/test": "^1.45.1",
|
||||
"@tauri-apps/cli": "==2.0.0-beta.13",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/node": "^18.19.31",
|
||||
"@types/pixelmatch": "^5.2.6",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
"@types/react": "^18.3.2",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@types/react-modal": "^3.16.3",
|
||||
"@types/three": "^0.163.0",
|
||||
"@types/ua-parser-js": "^0.7.39",
|
||||
@ -147,21 +138,27 @@
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||
"happy-dom": "^14.3.10",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^9.0.11",
|
||||
"node-fetch": "^3.3.2",
|
||||
"pixelmatch": "^5.3.0",
|
||||
"pngjs": "^7.0.0",
|
||||
"postcss": "^8.4.31",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier": "^2.8.8",
|
||||
"setimmediate": "^1.0.5",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"vite": "^5.2.9",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-package-version": "^1.1.0",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^1.6.0",
|
||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||
"wait-on": "^7.2.0",
|
||||
"wasm-pack": "^0.13.0",
|
||||
"ws": "^8.17.0",
|
||||
"yarn": "^1.22.22"
|
||||
}
|
||||
}
|
||||
|
6
packages/codemirror-lsp-client/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
build
|
||||
dist
|
||||
tsconfig.tsbuildinfo
|
||||
*.d.ts
|
||||
*.js
|
35
packages/codemirror-lsp-client/package.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@kittycad/codemirror-lsp-client",
|
||||
"version": "1.0.0",
|
||||
"description": "An LSP client for the codemirror editor.",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"types": "dist/index.d.ts",
|
||||
"module": "dist/index.js",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/KittyCAD/modeling-app",
|
||||
"author": "Zoo Engineering Team",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.16.3",
|
||||
"@codemirror/language": "^6.10.2",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@lezer/highlight": "^1.2.0",
|
||||
"@ts-stack/markdown": "^1.5.0",
|
||||
"json-rpc-2.0": "^1.7.0",
|
||||
"typescript": "^5.5.2",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"vscode-uri": "^3.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.14.9",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import * as vsrpc from 'vscode-jsonrpc'
|
||||
|
||||
import { Codec } from '.'
|
||||
import Bytes from './bytes'
|
||||
import PromiseMap from './map'
|
||||
import Queue from './queue'
|
||||
import Tracer from '../tracer'
|
||||
import { Codec } from '../codec'
|
||||
import Tracer from './tracer'
|
||||
import PromiseMap from './map'
|
||||
|
||||
export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
readonly responses: PromiseMap<number | string, vsrpc.ResponseMessage> =
|
||||
@ -15,9 +15,12 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
new Queue<vsrpc.RequestMessage>()
|
||||
|
||||
readonly #start: Promise<void>
|
||||
private trace: boolean = false
|
||||
|
||||
constructor() {
|
||||
constructor(trace?: boolean) {
|
||||
super()
|
||||
this.trace = trace || false
|
||||
|
||||
this.#start = this.start()
|
||||
}
|
||||
|
||||
@ -64,7 +67,10 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
contentLength = null
|
||||
|
||||
const message = JSON.parse(delimited) as vsrpc.Message
|
||||
Tracer.server(message)
|
||||
|
||||
if (this.trace) {
|
||||
Tracer.server(message)
|
||||
}
|
||||
|
||||
// demux the message stream
|
||||
if (vsrpc.Message.isResponse(message) && null != message.id) {
|
||||
@ -85,7 +91,9 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
|
||||
add(bytes: Uint8Array): void {
|
||||
const message = Codec.decode(bytes) as vsrpc.Message
|
||||
Tracer.server(message)
|
||||
if (this.trace) {
|
||||
Tracer.server(message)
|
||||
}
|
||||
|
||||
// demux the message stream
|
||||
if (vsrpc.Message.isResponse(message) && null != message.id) {
|
@ -1,12 +1,16 @@
|
||||
import * as jsrpc from 'json-rpc-2.0'
|
||||
import * as vsrpc from 'vscode-jsonrpc'
|
||||
|
||||
import Bytes from './codec/bytes'
|
||||
import StreamDemuxer from './codec/demuxer'
|
||||
import Headers from './codec/headers'
|
||||
import Queue from './codec/queue'
|
||||
import Bytes from './bytes'
|
||||
import StreamDemuxer from './demuxer'
|
||||
import Headers from './headers'
|
||||
import Queue from './queue'
|
||||
import Tracer from './tracer'
|
||||
import { LspWorkerEventType, LspWorker } from './types'
|
||||
|
||||
export enum LspWorkerEventType {
|
||||
Init = 'init',
|
||||
Call = 'call',
|
||||
}
|
||||
|
||||
export const encoder = new TextEncoder()
|
||||
export const decoder = new TextDecoder()
|
||||
@ -33,16 +37,24 @@ export class IntoServer
|
||||
implements AsyncGenerator<Uint8Array, never, void>
|
||||
{
|
||||
private worker: Worker | null = null
|
||||
private type_: LspWorker | null = null
|
||||
constructor(type_?: LspWorker, worker?: Worker) {
|
||||
private type_: String | null = null
|
||||
|
||||
private trace: boolean = false
|
||||
|
||||
constructor(type_?: String, worker?: Worker, trace?: boolean) {
|
||||
super()
|
||||
if (worker && type_) {
|
||||
this.worker = worker
|
||||
this.type_ = type_
|
||||
}
|
||||
|
||||
this.trace = trace || false
|
||||
}
|
||||
enqueue(item: Uint8Array): void {
|
||||
Tracer.client(Headers.remove(decoder.decode(item)))
|
||||
if (this.trace) {
|
||||
Tracer.client(Headers.remove(decoder.decode(item)))
|
||||
}
|
||||
|
||||
if (this.worker) {
|
||||
this.worker.postMessage({
|
||||
worker: this.type_,
|
||||
@ -71,7 +83,7 @@ export namespace FromServer {
|
||||
// Calls private method .start() which can throw.
|
||||
// This is an odd one of the bunch but try/catch seems most suitable here.
|
||||
try {
|
||||
return new StreamDemuxer()
|
||||
return new StreamDemuxer(false)
|
||||
} catch (e: any) {
|
||||
return e
|
||||
}
|
13
packages/codemirror-lsp-client/src/client/codec/tracer.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Message } from 'vscode-languageserver-protocol'
|
||||
|
||||
export default class Tracer {
|
||||
static client(message: string): void {
|
||||
console.log('lsp client message', message)
|
||||
}
|
||||
|
||||
static server(input: string | Message): void {
|
||||
const message: string =
|
||||
typeof input === 'string' ? input : JSON.stringify(input)
|
||||
console.log('lsp server message', message)
|
||||
}
|
||||
}
|
@ -1,16 +1,8 @@
|
||||
import type * as LSP from 'vscode-languageserver-protocol'
|
||||
import Client from './client'
|
||||
import { SemanticToken, deserializeTokens } from './kcl/semantic_tokens'
|
||||
import { LanguageServerPlugin } from 'editor/plugins/lsp/plugin'
|
||||
import { CopilotLspCompletionParams } from 'wasm-lib/kcl/bindings/CopilotLspCompletionParams'
|
||||
import { CopilotCompletionResponse } from 'wasm-lib/kcl/bindings/CopilotCompletionResponse'
|
||||
import { CopilotAcceptCompletionParams } from 'wasm-lib/kcl/bindings/CopilotAcceptCompletionParams'
|
||||
import { CopilotRejectCompletionParams } from 'wasm-lib/kcl/bindings/CopilotRejectCompletionParams'
|
||||
import { UpdateUnitsParams } from 'wasm-lib/kcl/bindings/UpdateUnitsParams'
|
||||
import { UpdateCanExecuteParams } from 'wasm-lib/kcl/bindings/UpdateCanExecuteParams'
|
||||
import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse'
|
||||
import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse'
|
||||
import { LspWorker } from './types'
|
||||
|
||||
import { FromServer, IntoServer } from './codec'
|
||||
import Client from './jsonrpc'
|
||||
import { LanguageServerPlugin } from '../plugin/lsp'
|
||||
|
||||
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/
|
||||
|
||||
@ -31,12 +23,6 @@ interface LSPRequestMap {
|
||||
LSP.TextEdit[] | null
|
||||
]
|
||||
'textDocument/foldingRange': [LSP.FoldingRangeParams, LSP.FoldingRange[]]
|
||||
'copilot/getCompletions': [
|
||||
CopilotLspCompletionParams,
|
||||
CopilotCompletionResponse
|
||||
]
|
||||
'kcl/updateUnits': [UpdateUnitsParams, UpdateUnitsResponse | null]
|
||||
'kcl/updateCanExecute': [UpdateCanExecuteParams, UpdateCanExecuteResponse]
|
||||
}
|
||||
|
||||
// Client to server
|
||||
@ -49,21 +35,13 @@ interface LSPNotifyMap {
|
||||
'workspace/didCreateFiles': LSP.CreateFilesParams
|
||||
'workspace/didRenameFiles': LSP.RenameFilesParams
|
||||
'workspace/didDeleteFiles': LSP.DeleteFilesParams
|
||||
'copilot/notifyAccepted': CopilotAcceptCompletionParams
|
||||
'copilot/notifyRejected': CopilotRejectCompletionParams
|
||||
}
|
||||
|
||||
export interface LanguageServerClientOptions {
|
||||
client: Client
|
||||
name: LspWorker
|
||||
}
|
||||
|
||||
export interface LanguageServerOptions {
|
||||
// We assume this is the main project directory, we are currently working in.
|
||||
workspaceFolders: LSP.WorkspaceFolder[]
|
||||
documentUri: string
|
||||
allowHTMLContent: boolean
|
||||
client: LanguageServerClient
|
||||
name: string
|
||||
fromServer: FromServer
|
||||
intoServer: IntoServer
|
||||
initializedCallback: () => void
|
||||
}
|
||||
|
||||
export class LanguageServerClient {
|
||||
@ -76,18 +54,18 @@ export class LanguageServerClient {
|
||||
|
||||
public initializePromise: Promise<void>
|
||||
|
||||
private isUpdatingSemanticTokens: boolean = false
|
||||
private semanticTokens: SemanticToken[] = []
|
||||
private queuedUids: string[] = []
|
||||
|
||||
constructor(options: LanguageServerClientOptions) {
|
||||
this.plugins = []
|
||||
this.client = options.client
|
||||
this.name = options.name
|
||||
this.plugins = []
|
||||
|
||||
this.client = new Client(
|
||||
options.fromServer,
|
||||
options.intoServer,
|
||||
options.initializedCallback
|
||||
)
|
||||
|
||||
this.ready = false
|
||||
|
||||
this.queuedUids = []
|
||||
this.initializePromise = this.initialize()
|
||||
}
|
||||
|
||||
@ -111,19 +89,10 @@ export class LanguageServerClient {
|
||||
|
||||
textDocumentDidOpen(params: LSP.DidOpenTextDocumentParams) {
|
||||
this.notify('textDocument/didOpen', params)
|
||||
|
||||
// Update the facet of the plugins to the correct value.
|
||||
for (const plugin of this.plugins) {
|
||||
plugin.documentUri = params.textDocument.uri
|
||||
plugin.languageId = params.textDocument.languageId
|
||||
}
|
||||
|
||||
this.updateSemanticTokens(params.textDocument.uri)
|
||||
}
|
||||
|
||||
textDocumentDidChange(params: LSP.DidChangeTextDocumentParams) {
|
||||
this.notify('textDocument/didChange', params)
|
||||
this.updateSemanticTokens(params.textDocument.uri)
|
||||
}
|
||||
|
||||
textDocumentDidClose(params: LSP.DidCloseTextDocumentParams) {
|
||||
@ -134,18 +103,9 @@ export class LanguageServerClient {
|
||||
added: LSP.WorkspaceFolder[],
|
||||
removed: LSP.WorkspaceFolder[]
|
||||
) {
|
||||
// Add all the current workspace folders in the plugin to removed.
|
||||
for (const plugin of this.plugins) {
|
||||
removed.push(...plugin.workspaceFolders)
|
||||
}
|
||||
this.notify('workspace/didChangeWorkspaceFolders', {
|
||||
event: { added, removed },
|
||||
})
|
||||
|
||||
// Add all the new workspace folders to the plugins.
|
||||
for (const plugin of this.plugins) {
|
||||
plugin.workspaceFolders = added
|
||||
}
|
||||
}
|
||||
|
||||
workspaceDidCreateFiles(params: LSP.CreateFilesParams) {
|
||||
@ -160,33 +120,13 @@ export class LanguageServerClient {
|
||||
this.notify('workspace/didDeleteFiles', params)
|
||||
}
|
||||
|
||||
async updateSemanticTokens(uri: string) {
|
||||
async textDocumentSemanticTokensFull(params: LSP.SemanticTokensParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.semanticTokensProvider) {
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure we can only run, if we aren't already running.
|
||||
if (!this.isUpdatingSemanticTokens) {
|
||||
this.isUpdatingSemanticTokens = true
|
||||
|
||||
const result = await this.request('textDocument/semanticTokens/full', {
|
||||
textDocument: {
|
||||
uri,
|
||||
},
|
||||
})
|
||||
|
||||
this.semanticTokens = await deserializeTokens(
|
||||
result.data,
|
||||
this.getServerCapabilities().semanticTokensProvider
|
||||
)
|
||||
|
||||
this.isUpdatingSemanticTokens = false
|
||||
}
|
||||
}
|
||||
|
||||
getSemanticTokens(): SemanticToken[] {
|
||||
return this.semanticTokens
|
||||
return this.request('textDocument/semanticTokens/full', params)
|
||||
}
|
||||
|
||||
async textDocumentHover(params: LSP.HoverParams) {
|
||||
@ -239,6 +179,10 @@ export class LanguageServerClient {
|
||||
return this.client.request(method, params) as Promise<LSPRequestMap[K][1]>
|
||||
}
|
||||
|
||||
requestCustom<P, R>(method: string, params: P): Promise<R> {
|
||||
return this.client.request(method, params) as Promise<R>
|
||||
}
|
||||
|
||||
private notify<K extends keyof LSPNotifyMap>(
|
||||
method: K,
|
||||
params: LSPNotifyMap[K]
|
||||
@ -246,44 +190,8 @@ export class LanguageServerClient {
|
||||
return this.client.notify(method, params)
|
||||
}
|
||||
|
||||
async getCompletion(params: CopilotLspCompletionParams) {
|
||||
const response = await this.request('copilot/getCompletions', params)
|
||||
//
|
||||
this.queuedUids = [...response.completions.map((c) => c.uuid)]
|
||||
return response
|
||||
}
|
||||
|
||||
async accept(uuid: string) {
|
||||
const badUids = this.queuedUids.filter((u) => u !== uuid)
|
||||
this.queuedUids = []
|
||||
this.acceptCompletion({ uuid })
|
||||
this.rejectCompletions({ uuids: badUids })
|
||||
}
|
||||
|
||||
async reject() {
|
||||
const badUids = this.queuedUids
|
||||
this.queuedUids = []
|
||||
this.rejectCompletions({ uuids: badUids })
|
||||
}
|
||||
|
||||
acceptCompletion(params: CopilotAcceptCompletionParams) {
|
||||
this.notify('copilot/notifyAccepted', params)
|
||||
}
|
||||
|
||||
rejectCompletions(params: CopilotRejectCompletionParams) {
|
||||
this.notify('copilot/notifyRejected', params)
|
||||
}
|
||||
|
||||
async updateUnits(
|
||||
params: UpdateUnitsParams
|
||||
): Promise<UpdateUnitsResponse | null> {
|
||||
return await this.request('kcl/updateUnits', params)
|
||||
}
|
||||
|
||||
async updateCanExecute(
|
||||
params: UpdateCanExecuteParams
|
||||
): Promise<UpdateCanExecuteResponse> {
|
||||
return await this.request('kcl/updateCanExecute', params)
|
||||
notifyCustom<P>(method: string, params: P): void {
|
||||
return this.client.notify(method, params)
|
||||
}
|
||||
|
||||
private processNotifications(notification: LSP.NotificationMessage) {
|
@ -6,7 +6,6 @@ import {
|
||||
unregisterServerCapability,
|
||||
} from './server-capability-registration'
|
||||
import { Codec, FromServer, IntoServer } from './codec'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
const client_capabilities: LSP.ClientCapabilities = {
|
||||
textDocument: {
|
||||
@ -67,8 +66,13 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
||||
#fromServer: FromServer
|
||||
private serverCapabilities: LSP.ServerCapabilities<any> = {}
|
||||
private notifyFn: ((message: LSP.NotificationMessage) => void) | null = null
|
||||
private initializedCallback: () => void
|
||||
|
||||
constructor(fromServer: FromServer, intoServer: IntoServer) {
|
||||
constructor(
|
||||
fromServer: FromServer,
|
||||
intoServer: IntoServer,
|
||||
initializedCallback: () => void
|
||||
) {
|
||||
super(
|
||||
new jsrpc.JSONRPCServer(),
|
||||
new jsrpc.JSONRPCClient(async (json: jsrpc.JSONRPCRequest) => {
|
||||
@ -82,6 +86,7 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
||||
})
|
||||
)
|
||||
this.#fromServer = fromServer
|
||||
this.initializedCallback = initializedCallback
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
@ -124,7 +129,9 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
||||
this.serverCapabilities,
|
||||
capabilityRegistration
|
||||
)
|
||||
if (err(caps)) return (this.serverCapabilities = {})
|
||||
if (caps instanceof Error) {
|
||||
return (this.serverCapabilities = {})
|
||||
}
|
||||
this.serverCapabilities = caps
|
||||
}
|
||||
)
|
||||
@ -139,7 +146,9 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
||||
this.serverCapabilities,
|
||||
capabilityUnregistration
|
||||
)
|
||||
if (err(caps)) return (this.serverCapabilities = {})
|
||||
if (caps instanceof Error) {
|
||||
return (this.serverCapabilities = {})
|
||||
}
|
||||
this.serverCapabilities = caps
|
||||
}
|
||||
)
|
||||
@ -151,7 +160,7 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
||||
{
|
||||
processId: null,
|
||||
clientInfo: {
|
||||
name: 'kcl-language-client',
|
||||
name: 'codemirror-lsp-client',
|
||||
},
|
||||
capabilities: client_capabilities,
|
||||
rootUri: null,
|
||||
@ -163,6 +172,8 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
||||
// notify "initialized": client --> server
|
||||
this.notify(LSP.InitializedNotification.type.method, {})
|
||||
|
||||
this.initializedCallback()
|
||||
|
||||
await Promise.all(
|
||||
this.afterInitializedHooks.map((f: () => Promise<void>) => f())
|
||||
)
|
57
packages/codemirror-lsp-client/src/index.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { foldService } from '@codemirror/language'
|
||||
import { Extension, EditorState } from '@codemirror/state'
|
||||
import { ViewPlugin } from '@codemirror/view'
|
||||
|
||||
import {
|
||||
docPathFacet,
|
||||
LanguageServerPlugin,
|
||||
LanguageServerPluginSpec,
|
||||
languageId,
|
||||
workspaceFolders,
|
||||
LanguageServerOptions,
|
||||
} from './plugin/lsp'
|
||||
|
||||
export type { LanguageServerClientOptions } from './client'
|
||||
export { LanguageServerClient } from './client'
|
||||
export {
|
||||
Codec,
|
||||
FromServer,
|
||||
IntoServer,
|
||||
LspWorkerEventType,
|
||||
} from './client/codec'
|
||||
export type { LanguageServerOptions } from './plugin/lsp'
|
||||
export {
|
||||
LanguageServerPlugin,
|
||||
LanguageServerPluginSpec,
|
||||
docPathFacet,
|
||||
languageId,
|
||||
workspaceFolders,
|
||||
lspSemanticTokensEvent,
|
||||
lspDiagnosticsEvent,
|
||||
lspFormatCodeEvent,
|
||||
} from './plugin/lsp'
|
||||
export { posToOffset, offsetToPos } from './plugin/util'
|
||||
|
||||
export function lspPlugin(options: LanguageServerOptions): Extension {
|
||||
let plugin: LanguageServerPlugin | null = null
|
||||
const viewPlugin = ViewPlugin.define(
|
||||
(view) => (plugin = new LanguageServerPlugin(options, view)),
|
||||
new LanguageServerPluginSpec()
|
||||
)
|
||||
|
||||
let ext = [
|
||||
docPathFacet.of(options.documentUri),
|
||||
languageId.of('kcl'),
|
||||
workspaceFolders.of(options.workspaceFolders),
|
||||
viewPlugin,
|
||||
foldService.of((state: EditorState, lineStart: number, lineEnd: number) => {
|
||||
if (plugin == null) return null
|
||||
// Get the folding ranges from the language server.
|
||||
// Since this is async we directly need to update the folding ranges after.
|
||||
const range = plugin?.foldingRange(lineStart, lineEnd)
|
||||
return range
|
||||
}),
|
||||
]
|
||||
|
||||
return ext
|
||||
}
|
112
packages/codemirror-lsp-client/src/plugin/autocomplete.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import {
|
||||
acceptCompletion,
|
||||
autocompletion,
|
||||
clearSnippet,
|
||||
closeCompletion,
|
||||
hasNextSnippetField,
|
||||
moveCompletionSelection,
|
||||
nextSnippetField,
|
||||
prevSnippetField,
|
||||
startCompletion,
|
||||
} from '@codemirror/autocomplete'
|
||||
import { Prec, Extension } from '@codemirror/state'
|
||||
import { EditorView, keymap, KeyBinding, ViewPlugin } from '@codemirror/view'
|
||||
|
||||
import {
|
||||
CompletionItemKind,
|
||||
CompletionTriggerKind,
|
||||
} from 'vscode-languageserver-protocol'
|
||||
|
||||
import { LanguageServerPlugin } from './lsp'
|
||||
import { offsetToPos } from './util'
|
||||
import { syntaxTree } from '@codemirror/language'
|
||||
|
||||
export const CompletionItemKindMap = Object.fromEntries(
|
||||
Object.entries(CompletionItemKind).map(([key, value]) => [value, key])
|
||||
) as Record<CompletionItemKind, string>
|
||||
|
||||
const lspAutocompleteKeymap: readonly KeyBinding[] = [
|
||||
{ key: 'Ctrl-Space', run: startCompletion },
|
||||
{
|
||||
key: 'Escape',
|
||||
run: (view: EditorView): boolean => {
|
||||
if (clearSnippet(view)) return true
|
||||
|
||||
return closeCompletion(view)
|
||||
},
|
||||
},
|
||||
{ key: 'ArrowDown', run: moveCompletionSelection(true) },
|
||||
{ key: 'ArrowUp', run: moveCompletionSelection(false) },
|
||||
{ key: 'PageDown', run: moveCompletionSelection(true, 'page') },
|
||||
{ key: 'PageUp', run: moveCompletionSelection(false, 'page') },
|
||||
{ key: 'Enter', run: acceptCompletion },
|
||||
{
|
||||
key: 'Tab',
|
||||
run: (view: EditorView): boolean => {
|
||||
if (hasNextSnippetField(view.state)) {
|
||||
const result = nextSnippetField(view)
|
||||
return result
|
||||
}
|
||||
|
||||
return acceptCompletion(view)
|
||||
},
|
||||
shift: prevSnippetField,
|
||||
},
|
||||
]
|
||||
|
||||
const lspAutocompleteKeymapExt = Prec.highest(keymap.of(lspAutocompleteKeymap))
|
||||
|
||||
export default function lspAutocompleteExt(
|
||||
plugin: ViewPlugin<LanguageServerPlugin>
|
||||
): Extension {
|
||||
return [
|
||||
lspAutocompleteKeymapExt,
|
||||
autocompletion({
|
||||
defaultKeymap: false,
|
||||
override: [
|
||||
async (context) => {
|
||||
const { state, pos, explicit, view } = context
|
||||
let value = view?.plugin(plugin)
|
||||
if (!value) return null
|
||||
|
||||
let nodeBefore = syntaxTree(state).resolveInner(pos, -1)
|
||||
if (
|
||||
nodeBefore.name === 'BlockComment' ||
|
||||
nodeBefore.name === 'LineComment'
|
||||
)
|
||||
return null
|
||||
|
||||
const line = state.doc.lineAt(pos)
|
||||
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
|
||||
let trigChar: string | undefined
|
||||
if (
|
||||
!explicit &&
|
||||
value.client
|
||||
.getServerCapabilities()
|
||||
.completionProvider?.triggerCharacters?.includes(
|
||||
line.text[pos - line.from - 1]
|
||||
)
|
||||
) {
|
||||
trigKind = CompletionTriggerKind.TriggerCharacter
|
||||
trigChar = line.text[pos - line.from - 1]
|
||||
}
|
||||
if (
|
||||
trigKind === CompletionTriggerKind.Invoked &&
|
||||
!context.matchBefore(/\w+$/)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return await value.requestCompletion(
|
||||
context,
|
||||
offsetToPos(state.doc, pos),
|
||||
{
|
||||
triggerKind: trigKind,
|
||||
triggerCharacter: trigChar,
|
||||
}
|
||||
)
|
||||
},
|
||||
],
|
||||
}),
|
||||
]
|
||||
}
|
27
packages/codemirror-lsp-client/src/plugin/format.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Extension, Prec } from '@codemirror/state'
|
||||
import { EditorView, keymap, KeyBinding, ViewPlugin } from '@codemirror/view'
|
||||
|
||||
import { LanguageServerPlugin } from './lsp'
|
||||
|
||||
export default function lspFormatExt(
|
||||
plugin: ViewPlugin<LanguageServerPlugin>
|
||||
): Extension {
|
||||
const formatKeymap: readonly KeyBinding[] = [
|
||||
{
|
||||
key: 'Alt-Shift-f',
|
||||
run: (view: EditorView) => {
|
||||
let value = view.plugin(plugin)
|
||||
if (!value) return false
|
||||
value.requestFormatting()
|
||||
return true
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// Create an extension for the key mappings.
|
||||
const formatKeymapExt = Prec.highest(
|
||||
keymap.computeN([], () => [formatKeymap])
|
||||
)
|
||||
|
||||
return formatKeymapExt
|
||||
}
|
22
packages/codemirror-lsp-client/src/plugin/hover.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Extension } from '@codemirror/state'
|
||||
import { hoverTooltip, tooltips, ViewPlugin } from '@codemirror/view'
|
||||
|
||||
import { LanguageServerPlugin } from './lsp'
|
||||
import { offsetToPos } from './util'
|
||||
|
||||
export default function lspHoverExt(
|
||||
plugin: ViewPlugin<LanguageServerPlugin>
|
||||
): Extension {
|
||||
return [
|
||||
hoverTooltip((view, pos) => {
|
||||
const value = view.plugin(plugin)
|
||||
return (
|
||||
value?.requestHoverTooltip(view, offsetToPos(view.state.doc, pos)) ??
|
||||
null
|
||||
)
|
||||
}),
|
||||
tooltips({
|
||||
position: 'absolute',
|
||||
}),
|
||||
]
|
||||
}
|
21
packages/codemirror-lsp-client/src/plugin/indent.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { indentService } from '@codemirror/language'
|
||||
import { Extension } from '@codemirror/state'
|
||||
|
||||
export default function lspIndentExt(): Extension {
|
||||
// Match the indentation of the previous line (if present).
|
||||
return indentService.of((context, pos) => {
|
||||
try {
|
||||
const previousLine = context.lineAt(pos, -1)
|
||||
const previousLineText = previousLine.text.replaceAll(
|
||||
'\t',
|
||||
' '.repeat(context.state.tabSize)
|
||||
)
|
||||
const match = previousLineText.match(/^(\s)*/)
|
||||
if (match === null || match.length <= 0) return null
|
||||
return match[0].length
|
||||
} catch (err) {
|
||||
console.error('Error in codemirror indentService', err)
|
||||
}
|
||||
return null
|
||||
})
|
||||
}
|
12
packages/codemirror-lsp-client/src/plugin/lint.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Extension } from '@codemirror/state'
|
||||
import { linter, forEachDiagnostic, Diagnostic } from '@codemirror/lint'
|
||||
|
||||
export default function lspLintExt(): Extension {
|
||||
return linter((view) => {
|
||||
let diagnostics: Diagnostic[] = []
|
||||
forEachDiagnostic(view.state, (d: Diagnostic, from: number, to: number) => {
|
||||
diagnostics.push(d)
|
||||
})
|
||||
return diagnostics
|
||||
})
|
||||
}
|
@ -1,115 +1,155 @@
|
||||
import { completeFromList, snippetCompletion } from '@codemirror/autocomplete'
|
||||
import { setDiagnostics } from '@codemirror/lint'
|
||||
import { Facet } from '@codemirror/state'
|
||||
import { EditorView, Tooltip } from '@codemirror/view'
|
||||
import {
|
||||
DiagnosticSeverity,
|
||||
CompletionItemKind,
|
||||
CompletionTriggerKind,
|
||||
} from 'vscode-languageserver-protocol'
|
||||
|
||||
import { deferExecution } from 'lib/utils'
|
||||
import type {
|
||||
Completion,
|
||||
CompletionContext,
|
||||
CompletionResult,
|
||||
} from '@codemirror/autocomplete'
|
||||
import { completeFromList, snippetCompletion } from '@codemirror/autocomplete'
|
||||
import {
|
||||
Facet,
|
||||
StateEffect,
|
||||
Extension,
|
||||
Transaction,
|
||||
Annotation,
|
||||
} from '@codemirror/state'
|
||||
import type {
|
||||
ViewUpdate,
|
||||
PluginValue,
|
||||
PluginSpec,
|
||||
ViewPlugin,
|
||||
} from '@codemirror/view'
|
||||
import { EditorView, Tooltip } from '@codemirror/view'
|
||||
|
||||
import type { PublishDiagnosticsParams } from 'vscode-languageserver-protocol'
|
||||
import type { ViewUpdate, PluginValue } from '@codemirror/view'
|
||||
import type * as LSP from 'vscode-languageserver-protocol'
|
||||
import { LanguageServerClient } from 'editor/plugins/lsp'
|
||||
import { Marked } from '@ts-stack/markdown'
|
||||
import { posToOffset } from 'editor/plugins/lsp/util'
|
||||
import { Program, ProgramMemory } from 'lang/wasm'
|
||||
import { codeManager, editorManager, kclManager } from 'lib/singletons'
|
||||
import type { UnitLength } from 'wasm-lib/kcl/bindings/UnitLength'
|
||||
import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse'
|
||||
import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse'
|
||||
import {
|
||||
DiagnosticSeverity,
|
||||
CompletionTriggerKind,
|
||||
} from 'vscode-languageserver-protocol'
|
||||
import { URI } from 'vscode-uri'
|
||||
|
||||
import { LanguageServerClient } from '../client'
|
||||
import { CompletionItemKindMap } from './autocomplete'
|
||||
import { addToken, SemanticToken } from './semantic-tokens'
|
||||
import { deferExecution, posToOffset, formatMarkdownContents } from './util'
|
||||
import lspAutocompleteExt from './autocomplete'
|
||||
import lspHoverExt from './hover'
|
||||
import lspFormatExt from './format'
|
||||
import lspIndentExt from './indent'
|
||||
import lspLintExt from './lint'
|
||||
import lspSemanticTokensExt from './semantic-tokens'
|
||||
|
||||
const useLast = (values: readonly any[]) => values.reduce((_, v) => v, '')
|
||||
export const documentUri = Facet.define<string, string>({ combine: useLast })
|
||||
export const docPathFacet = Facet.define<string, string>({
|
||||
combine: useLast,
|
||||
})
|
||||
export const languageId = Facet.define<string, string>({ combine: useLast })
|
||||
export const workspaceFolders = Facet.define<
|
||||
LSP.WorkspaceFolder[],
|
||||
LSP.WorkspaceFolder[]
|
||||
>({ combine: useLast })
|
||||
|
||||
const CompletionItemKindMap = Object.fromEntries(
|
||||
Object.entries(CompletionItemKind).map(([key, value]) => [value, key])
|
||||
) as Record<CompletionItemKind, string>
|
||||
export enum LspAnnotation {
|
||||
SemanticTokens = 'semantic-tokens',
|
||||
FormatCode = 'format-code',
|
||||
Diagnostics = 'diagnostics',
|
||||
}
|
||||
|
||||
const changesDelay = 600
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
const updateDelay = 100
|
||||
const lspEvent = Annotation.define<LspAnnotation>()
|
||||
export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens)
|
||||
export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode)
|
||||
export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics)
|
||||
|
||||
export interface LanguageServerOptions {
|
||||
// We assume this is the main project directory, we are currently working in.
|
||||
workspaceFolders: LSP.WorkspaceFolder[]
|
||||
documentUri: string
|
||||
allowHTMLContent: boolean
|
||||
client: LanguageServerClient
|
||||
processLspNotification?: (
|
||||
plugin: LanguageServerPlugin,
|
||||
notification: LSP.NotificationMessage
|
||||
) => void
|
||||
|
||||
changesDelay?: number
|
||||
}
|
||||
|
||||
export class LanguageServerPlugin implements PluginValue {
|
||||
public client: LanguageServerClient
|
||||
public documentUri: string
|
||||
public languageId: string
|
||||
public workspaceFolders: LSP.WorkspaceFolder[]
|
||||
private documentVersion: number
|
||||
private foldingRanges: LSP.FoldingRange[] | null = null
|
||||
private viewUpdate: ViewUpdate | null = null
|
||||
|
||||
private previousSemanticTokens: SemanticToken[] = []
|
||||
|
||||
private allowHTMLContent: boolean = true
|
||||
private changesDelay: number = 600
|
||||
private processLspNotification?: (
|
||||
plugin: LanguageServerPlugin,
|
||||
notification: LSP.NotificationMessage
|
||||
) => void
|
||||
|
||||
private _defferer = deferExecution((code: string) => {
|
||||
try {
|
||||
// Update the state (not the editor) with the new code.
|
||||
this.client.textDocumentDidChange({
|
||||
textDocument: {
|
||||
uri: this.documentUri,
|
||||
uri: this.getDocUri(),
|
||||
version: this.documentVersion++,
|
||||
},
|
||||
contentChanges: [{ text: code }],
|
||||
})
|
||||
|
||||
if (this.viewUpdate) {
|
||||
editorManager.handleOnViewUpdate(this.viewUpdate)
|
||||
}
|
||||
this.requestSemanticTokens()
|
||||
this.updateFoldingRanges()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}, changesDelay)
|
||||
}, this.changesDelay)
|
||||
|
||||
constructor(
|
||||
client: LanguageServerClient,
|
||||
private view: EditorView,
|
||||
private allowHTMLContent: boolean
|
||||
) {
|
||||
this.client = client
|
||||
this.documentUri = this.view.state.facet(documentUri)
|
||||
this.languageId = this.view.state.facet(languageId)
|
||||
this.workspaceFolders = this.view.state.facet(workspaceFolders)
|
||||
constructor(options: LanguageServerOptions, private view: EditorView) {
|
||||
this.client = options.client
|
||||
this.documentVersion = 0
|
||||
|
||||
if (options.changesDelay) {
|
||||
this.changesDelay = options.changesDelay
|
||||
}
|
||||
|
||||
if (options.allowHTMLContent !== undefined) {
|
||||
this.allowHTMLContent = options.allowHTMLContent
|
||||
}
|
||||
|
||||
this.client.attachPlugin(this)
|
||||
|
||||
this.processLspNotification = options.processLspNotification
|
||||
|
||||
this.initialize({
|
||||
documentText: this.view.state.doc.toString(),
|
||||
documentText: this.getDocText(),
|
||||
})
|
||||
}
|
||||
|
||||
update(viewUpdate: ViewUpdate) {
|
||||
this.viewUpdate = viewUpdate
|
||||
if (!viewUpdate.docChanged) {
|
||||
// debounce the view update.
|
||||
// otherwise it is laggy for typing.
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer)
|
||||
}
|
||||
private getDocPath(view = this.view) {
|
||||
return view.state.facet(docPathFacet)
|
||||
}
|
||||
|
||||
debounceTimer = setTimeout(() => {
|
||||
editorManager.handleOnViewUpdate(viewUpdate)
|
||||
}, updateDelay)
|
||||
private getDocText(view = this.view) {
|
||||
return view.state.doc.toString()
|
||||
}
|
||||
|
||||
private getDocUri(view = this.view) {
|
||||
return URI.file(this.getDocPath(view)).toString()
|
||||
}
|
||||
|
||||
private getLanguageId(view = this.view) {
|
||||
return view.state.facet(languageId)
|
||||
}
|
||||
|
||||
update(viewUpdate: ViewUpdate) {
|
||||
// If the doc didn't change we can return early.
|
||||
if (!viewUpdate.docChanged) {
|
||||
return
|
||||
}
|
||||
|
||||
const newCode = this.view.state.doc.toString()
|
||||
|
||||
codeManager.code = newCode
|
||||
codeManager.writeToFile()
|
||||
kclManager.executeCode()
|
||||
|
||||
this.sendChange({
|
||||
documentText: newCode,
|
||||
documentText: viewUpdate.state.doc.toString(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -121,14 +161,18 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
if (this.client.initializePromise) {
|
||||
await this.client.initializePromise
|
||||
}
|
||||
|
||||
this.client.textDocumentDidOpen({
|
||||
textDocument: {
|
||||
uri: this.documentUri,
|
||||
languageId: this.languageId,
|
||||
uri: this.getDocUri(),
|
||||
languageId: this.getLanguageId(),
|
||||
text: documentText,
|
||||
version: this.documentVersion,
|
||||
},
|
||||
})
|
||||
|
||||
this.requestSemanticTokens()
|
||||
this.updateFoldingRanges()
|
||||
}
|
||||
|
||||
async sendChange({ documentText }: { documentText: string }) {
|
||||
@ -137,8 +181,8 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
this._defferer(documentText)
|
||||
}
|
||||
|
||||
requestDiagnostics(view: EditorView) {
|
||||
this.sendChange({ documentText: view.state.doc.toString() })
|
||||
requestDiagnostics() {
|
||||
this.sendChange({ documentText: this.getDocText() })
|
||||
}
|
||||
|
||||
async requestHoverTooltip(
|
||||
@ -151,9 +195,9 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
)
|
||||
return null
|
||||
|
||||
this.sendChange({ documentText: view.state.doc.toString() })
|
||||
this.sendChange({ documentText: this.getDocText() })
|
||||
const result = await this.client.textDocumentHover({
|
||||
textDocument: { uri: this.documentUri },
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
position: { line, character },
|
||||
})
|
||||
if (!result) return null
|
||||
@ -169,8 +213,8 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
dom.classList.add('documentation')
|
||||
dom.classList.add('hover-tooltip')
|
||||
dom.style.zIndex = '99999999'
|
||||
if (this.allowHTMLContent) dom.innerHTML = formatContents(contents)
|
||||
else dom.textContent = formatContents(contents)
|
||||
if (this.allowHTMLContent) dom.innerHTML = formatMarkdownContents(contents)
|
||||
else dom.textContent = formatMarkdownContents(contents)
|
||||
return { pos, end, create: (view) => ({ dom }), above: true }
|
||||
}
|
||||
|
||||
@ -180,8 +224,9 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
!this.client.getServerCapabilities().foldingRangeProvider
|
||||
)
|
||||
return null
|
||||
|
||||
const result = await this.client.textDocumentFoldingRange({
|
||||
textDocument: { uri: this.documentUri },
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
})
|
||||
|
||||
return result || null
|
||||
@ -222,42 +267,6 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
return null
|
||||
}
|
||||
|
||||
async updateUnits(units: UnitLength): Promise<UpdateUnitsResponse | null> {
|
||||
if (this.client.name !== 'kcl') return null
|
||||
if (!this.client.ready) return null
|
||||
|
||||
return await this.client.updateUnits({
|
||||
textDocument: {
|
||||
uri: this.documentUri,
|
||||
},
|
||||
text: this.view.state.doc.toString(),
|
||||
units,
|
||||
})
|
||||
}
|
||||
async updateCanExecute(
|
||||
canExecute: boolean
|
||||
): Promise<UpdateCanExecuteResponse | null> {
|
||||
if (this.client.name !== 'kcl') return null
|
||||
if (!this.client.ready) return null
|
||||
|
||||
let response = await this.client.updateCanExecute({
|
||||
canExecute,
|
||||
})
|
||||
|
||||
if (!canExecute && response.isExecuting) {
|
||||
// We want to wait until the server is not busy before we reply to the
|
||||
// caller.
|
||||
while (response.isExecuting) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
response = await this.client.updateCanExecute({
|
||||
canExecute,
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log('[lsp] kcl: updated canExecute', canExecute, response)
|
||||
return response
|
||||
}
|
||||
|
||||
async requestFormatting() {
|
||||
if (
|
||||
!this.client.ready ||
|
||||
@ -267,14 +276,14 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
|
||||
this.client.textDocumentDidChange({
|
||||
textDocument: {
|
||||
uri: this.documentUri,
|
||||
uri: this.getDocUri(),
|
||||
version: this.documentVersion++,
|
||||
},
|
||||
contentChanges: [{ text: this.view.state.doc.toString() }],
|
||||
contentChanges: [{ text: this.getDocText() }],
|
||||
})
|
||||
|
||||
const result = await this.client.textDocumentFormatting({
|
||||
textDocument: { uri: this.documentUri },
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
options: {
|
||||
tabSize: 2,
|
||||
insertSpaces: true,
|
||||
@ -282,20 +291,16 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
},
|
||||
})
|
||||
|
||||
if (!result) return null
|
||||
if (!result || !result.length) return null
|
||||
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
const { range, newText } = result[i]
|
||||
this.view.dispatch({
|
||||
changes: [
|
||||
{
|
||||
from: posToOffset(this.view.state.doc, range.start)!,
|
||||
to: posToOffset(this.view.state.doc, range.end)!,
|
||||
insert: newText,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
this.view.dispatch({
|
||||
changes: result.map(({ range, newText }) => ({
|
||||
from: posToOffset(this.view.state.doc, range.start)!,
|
||||
to: posToOffset(this.view.state.doc, range.end)!,
|
||||
insert: newText,
|
||||
})),
|
||||
annotations: lspFormatCodeEvent,
|
||||
})
|
||||
}
|
||||
|
||||
async requestCompletion(
|
||||
@ -320,7 +325,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
})
|
||||
|
||||
const result = await this.client.textDocumentCompletion({
|
||||
textDocument: { uri: this.documentUri },
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
position: { line, character },
|
||||
context: {
|
||||
triggerKind,
|
||||
@ -360,7 +365,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
}
|
||||
if (documentation) {
|
||||
completion.info = () => {
|
||||
const htmlString = formatContents(documentation)
|
||||
const htmlString = formatMarkdownContents(documentation)
|
||||
const htmlNode = document.createElement('div')
|
||||
htmlNode.style.display = 'contents'
|
||||
htmlNode.innerHTML = htmlString
|
||||
@ -379,16 +384,107 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
return completeFromList(options)(context)
|
||||
}
|
||||
|
||||
parseSemanticTokens(view: EditorView, data: number[]) {
|
||||
// decode the lsp semantic token types
|
||||
const tokens = []
|
||||
for (let i = 0; i < data.length; i += 5) {
|
||||
tokens.push({
|
||||
deltaLine: data[i],
|
||||
startChar: data[i + 1],
|
||||
length: data[i + 2],
|
||||
tokenType: data[i + 3],
|
||||
modifiers: data[i + 4],
|
||||
})
|
||||
}
|
||||
|
||||
// convert the tokens into an array of {to, from, type} objects
|
||||
const tokenTypes =
|
||||
this.client.getServerCapabilities().semanticTokensProvider!.legend
|
||||
.tokenTypes
|
||||
const tokenModifiers =
|
||||
this.client.getServerCapabilities().semanticTokensProvider!.legend
|
||||
.tokenModifiers
|
||||
const tokenRanges: any = []
|
||||
let curLine = 0
|
||||
let prevStart = 0
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i]
|
||||
const tokenType = tokenTypes[token.tokenType]
|
||||
// get a list of modifiers
|
||||
const tokenModifier = []
|
||||
for (let j = 0; j < tokenModifiers.length; j++) {
|
||||
if (token.modifiers & (1 << j)) {
|
||||
tokenModifier.push(tokenModifiers[j])
|
||||
}
|
||||
}
|
||||
|
||||
if (token.deltaLine !== 0) prevStart = 0
|
||||
|
||||
const tokenRange = {
|
||||
from: posToOffset(view.state.doc, {
|
||||
line: curLine + token.deltaLine,
|
||||
character: prevStart + token.startChar,
|
||||
})!,
|
||||
to: posToOffset(view.state.doc, {
|
||||
line: curLine + token.deltaLine,
|
||||
character: prevStart + token.startChar + token.length,
|
||||
})!,
|
||||
type: tokenType,
|
||||
modifiers: tokenModifier,
|
||||
}
|
||||
tokenRanges.push(tokenRange)
|
||||
|
||||
curLine += token.deltaLine
|
||||
prevStart += token.startChar
|
||||
}
|
||||
|
||||
// sort by from
|
||||
tokenRanges.sort((a: any, b: any) => a.from - b.from)
|
||||
return tokenRanges
|
||||
}
|
||||
|
||||
async requestSemanticTokens() {
|
||||
if (
|
||||
!this.client.ready ||
|
||||
!this.client.getServerCapabilities().semanticTokensProvider
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
const result = await this.client.textDocumentSemanticTokensFull({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
})
|
||||
if (!result) return null
|
||||
|
||||
const { data } = result
|
||||
this.previousSemanticTokens = this.parseSemanticTokens(this.view, data)
|
||||
|
||||
const effects: StateEffect<SemanticToken | Extension>[] =
|
||||
this.previousSemanticTokens.map((tokenRange: any) =>
|
||||
addToken.of(tokenRange)
|
||||
)
|
||||
|
||||
this.view.dispatch({
|
||||
effects,
|
||||
|
||||
annotations: [lspSemanticTokensEvent, Transaction.addToHistory.of(false)],
|
||||
})
|
||||
}
|
||||
|
||||
async processNotification(notification: LSP.NotificationMessage) {
|
||||
try {
|
||||
switch (notification.method) {
|
||||
case 'textDocument/publishDiagnostics':
|
||||
if (notification === undefined) break
|
||||
if (notification.params === undefined) break
|
||||
if (!notification.params) break
|
||||
const params = notification.params as PublishDiagnosticsParams
|
||||
if (!params) break
|
||||
console.log(
|
||||
'[lsp] [window/publishDiagnostics]',
|
||||
this.client.getName(),
|
||||
notification.params
|
||||
params
|
||||
)
|
||||
const params = notification.params as PublishDiagnosticsParams
|
||||
// this is sometimes slower than our actual typing.
|
||||
this.processDiagnostics(params)
|
||||
break
|
||||
@ -406,30 +502,17 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
notification.params
|
||||
)
|
||||
break
|
||||
case 'kcl/astUpdated':
|
||||
// The server has updated the AST, we should update elsewhere.
|
||||
let updatedAst = notification.params as Program
|
||||
console.log('[lsp]: Updated AST', updatedAst)
|
||||
|
||||
// Update the folding ranges, since the AST has changed.
|
||||
// This is a hack since codemirror does not support async foldService.
|
||||
// When they do we can delete this.
|
||||
this.updateFoldingRanges()
|
||||
break
|
||||
case 'kcl/memoryUpdated':
|
||||
// The server has updated the memory, we should update elsewhere.
|
||||
let updatedMemory = notification.params as ProgramMemory
|
||||
console.log('[lsp]: Updated Memory', updatedMemory)
|
||||
kclManager.programMemory = updatedMemory
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
// Send it to the plugin
|
||||
this.processLspNotification?.(this, notification)
|
||||
}
|
||||
|
||||
processDiagnostics(params: PublishDiagnosticsParams) {
|
||||
if (params.uri !== this.documentUri) return
|
||||
if (params.uri !== this.getDocUri()) return
|
||||
|
||||
const diagnostics = params.diagnostics
|
||||
.map(({ range, message, severity }) => ({
|
||||
@ -459,18 +542,26 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
return 0
|
||||
})
|
||||
|
||||
this.view.dispatch(setDiagnostics(this.view.state, diagnostics))
|
||||
/* This creates infighting with the others.
|
||||
* TODO: turn it back on when we have a better way to handle it.
|
||||
* this.view.dispatch({
|
||||
effects: [setDiagnosticsEffect.of(diagnostics)],
|
||||
annotations: [lspDiagnosticsEvent, Transaction.addToHistory.of(false)],
|
||||
})*/
|
||||
}
|
||||
}
|
||||
|
||||
function formatContents(
|
||||
contents: LSP.MarkupContent | LSP.MarkedString | LSP.MarkedString[]
|
||||
): string {
|
||||
if (Array.isArray(contents)) {
|
||||
return contents.map((c) => formatContents(c) + '\n\n').join('')
|
||||
} else if (typeof contents === 'string') {
|
||||
return Marked.parse(contents)
|
||||
} else {
|
||||
return Marked.parse(contents.value)
|
||||
export class LanguageServerPluginSpec
|
||||
implements PluginSpec<LanguageServerPlugin>
|
||||
{
|
||||
provide(plugin: ViewPlugin<LanguageServerPlugin>): Extension {
|
||||
return [
|
||||
lspAutocompleteExt(plugin),
|
||||
lspFormatExt(plugin),
|
||||
lspHoverExt(plugin),
|
||||
lspIndentExt(),
|
||||
lspLintExt(),
|
||||
lspSemanticTokensExt(),
|
||||
]
|
||||
}
|
||||
}
|