Compare commits
133 Commits
pierremtb/
...
pierremtb/
Author | SHA1 | Date | |
---|---|---|---|
9407162543 | |||
067b83b468 | |||
ff2103d493 | |||
2dfa8f2176 | |||
29ed330326 | |||
c5b30341eb | |||
ca2cc825a6 | |||
3e6441b563 | |||
acafcf2d4d | |||
83fe1b7ce0 | |||
157b76cc78 | |||
cf957d880e | |||
dfc3d19677 | |||
dd370a9365 | |||
2274d6459c | |||
32ce857119 | |||
88b51da417 | |||
30d365aeb3 | |||
7af62399ac | |||
441d957228 | |||
9e57034873 | |||
eb96d6539c | |||
513c76ecc8 | |||
b9f31d94d5 | |||
51d9449280 | |||
9e03b58ae5 | |||
c591f73c70 | |||
9330aaba13 | |||
5a14f0189e | |||
9af001f22e | |||
435d1ea52e | |||
6366bc4766 | |||
7a21918223 | |||
54847139f2 | |||
9369a17ea7 | |||
8072f1db63 | |||
9ee2e7c3b0 | |||
28ae261e5f | |||
e4b0de0ead | |||
12859598a3 | |||
e9a334f433 | |||
18e1855fa9 | |||
7be53c7d4a | |||
e470a7b4af | |||
2bf20988ef | |||
602c39f63c | |||
4994aa6f61 | |||
1495cc6d18 | |||
4aa07b81db | |||
f876e6ca3c | |||
89ef4b3243 | |||
001c9a8219 | |||
60a0c811ab | |||
cab0c1e6a1 | |||
4dde3f60e0 | |||
f407c53032 | |||
417d720b22 | |||
9ba584487a | |||
77293952c0 | |||
96b66d6bca | |||
8d66f3ffad | |||
f4c54cbbe4 | |||
ea3d604b73 | |||
023a659491 | |||
5156b847f3 | |||
ded9f2c56b | |||
5db5f79f9a | |||
6a883f4a8d | |||
dd3a2b14f9 | |||
07b91f0fb1 | |||
1b2e213afe | |||
f48a23c35e | |||
7e1d102496 | |||
94cb2535c0 | |||
9e08ec9096 | |||
17bd8ec32a | |||
d0f12e85e5 | |||
94d185944e | |||
c38b2270c3 | |||
967ad66c98 | |||
afeca9ca39 | |||
61242282f0 | |||
0065df13ce | |||
01c8d45c13 | |||
8b25527f21 | |||
2abd980de9 | |||
f783deb706 | |||
f4dd295ca1 | |||
ceaa85fe3f | |||
3991bd9173 | |||
b8f9da36c0 | |||
283315b5d2 | |||
e204dfe564 | |||
208a36196b | |||
424b409cc1 | |||
82a58e69c2 | |||
776b420031 | |||
1087d4223b | |||
660a349588 | |||
089d6df889 | |||
56c37da317 | |||
efb067af58 | |||
2aa27eab01 | |||
a46734b76d | |||
4347e0cf84 | |||
9c47ac5b57 | |||
df3e541cdf | |||
b1cec443b9 | |||
5ae1aecd74 | |||
68ae7e98f9 | |||
56771d561a | |||
f09411817c | |||
bed7ae3b8b | |||
c43510732c | |||
51f0b669a4 | |||
967f49055d | |||
fe977524b5 | |||
3cbedcd3e7 | |||
5d2fa43150 | |||
d6f271fb0f | |||
e851b2bcc4 | |||
be569c91de | |||
5080e304b9 | |||
f4e75b7b4f | |||
31cbc90f56 | |||
a7d3552472 | |||
e984b20664 | |||
0c2cd24bda | |||
9b2de237b8 | |||
c79c02f18e | |||
4851aa2d71 | |||
76fafa6fd0 | |||
647ca11e08 |
26
.github/workflows/build-apps.yml
vendored
@ -362,6 +362,17 @@ jobs:
|
|||||||
- name: List artifacts
|
- name: List artifacts
|
||||||
run: "ls -R out"
|
run: "ls -R out"
|
||||||
|
|
||||||
|
- name: Set more complete nightly release notes
|
||||||
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
|
run: |
|
||||||
|
# Note: preferred going this way instead of a full clone in the checkout step,
|
||||||
|
# see https://github.com/actions/checkout/issues/1471
|
||||||
|
git fetch --prune --unshallow --tags
|
||||||
|
export TAG="nightly-${VERSION}"
|
||||||
|
export PREVIOUS_TAG=$(git describe --tags --match="nightly-v[0-9]*" --abbrev=0)
|
||||||
|
export NOTES=$(./scripts/get-nightly-changelog.sh)
|
||||||
|
yarn files:set-notes
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: 'google-github-actions/auth@v2.1.7'
|
uses: 'google-github-actions/auth@v2.1.7'
|
||||||
@ -382,3 +393,18 @@ jobs:
|
|||||||
glob: '*'
|
glob: '*'
|
||||||
parent: false
|
parent: false
|
||||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||||
|
|
||||||
|
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||||
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
|
run: yarn files:invalidate-bucket:nightly
|
||||||
|
|
||||||
|
- name: Tag nightly commit
|
||||||
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { VERSION } = process.env
|
||||||
|
const { owner, repo } = context.repo
|
||||||
|
const { sha } = context
|
||||||
|
const ref = `refs/tags/nightly-${VERSION}`
|
||||||
|
github.rest.git.createRef({ owner, repo, sha, ref })
|
||||||
|
4
.github/workflows/e2e-tests.yml
vendored
@ -68,7 +68,7 @@ jobs:
|
|||||||
- name: Download Wasm Cache
|
- name: Download Wasm Cache
|
||||||
id: download-wasm
|
id: download-wasm
|
||||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v7
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
@ -255,7 +255,7 @@ jobs:
|
|||||||
- name: Download Wasm Cache
|
- name: Download Wasm Cache
|
||||||
id: download-wasm
|
id: download-wasm
|
||||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v7
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
6
.github/workflows/publish-apps-release.yml
vendored
@ -126,11 +126,7 @@ jobs:
|
|||||||
destination: 'dl.kittycad.io/releases/modeling-app'
|
destination: 'dl.kittycad.io/releases/modeling-app'
|
||||||
|
|
||||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||||
run: |
|
run: yarn files:invalidate-bucket
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/last_download.json" --async
|
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-linux-arm64.yml" --async
|
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
|
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
|
|
||||||
|
|
||||||
- name: Upload release files to Github
|
- name: Upload release files to Github
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
1
.gitignore
vendored
@ -61,6 +61,7 @@ Mac_App_Distribution.provisionprofile
|
|||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
src/wasm-lib/pkg
|
src/wasm-lib/pkg
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
venv
|
venv
|
||||||
.vite/
|
.vite/
|
||||||
|
|
||||||
|
43
INSTALL.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Setting Up Zoo Modeling App
|
||||||
|
|
||||||
|
Compared to other CAD software, getting Zoo Modeling App up and running is quick and straightforward across platforms. It's about 100MB to download and is quick to install.
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
|
||||||
|
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
|
||||||
|
|
||||||
|
2. Once downloaded, run the installer `Zoo Modeling App-{version}-{arch}-win.exe` which should take a few seconds.
|
||||||
|
|
||||||
|
3. The installation happens at `C:\Program Files\Zoo Modeling App`. A shortcut in the start menu is also created so you can run the app easily by clicking on it.
|
||||||
|
|
||||||
|
## macOS
|
||||||
|
|
||||||
|
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
|
||||||
|
|
||||||
|
2. Once downloaded, open the disk image `Zoo Modeling App-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
||||||
|
|
||||||
|
3. You can then open your `Applications` directory and double-click on `Zoo Modeling App` to open.
|
||||||
|
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
|
||||||
|
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
|
||||||
|
|
||||||
|
2. Install the dependencies needed to run the [AppImage format](https://appimage.org/).
|
||||||
|
- On Ubuntu, install the FUSE library with these commands in a terminal.
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install libfuse2
|
||||||
|
```
|
||||||
|
- Optionally, follow [these steps](https://github.com/probonopd/go-appimage/blob/master/src/appimaged/README.md#initial-setup) to install `appimaged`. It is a daemon that makes interacting with AppImage files more seamless.
|
||||||
|
- Once installed, copy the downloaded `Zoo Modeling App-{version}-{arch}-linux.AppImage` to the directory of your choice, for instance `~/Applications`.
|
||||||
|
|
||||||
|
- `appimaged` should automatically find it and make it executable. If not, run:
|
||||||
|
```bash
|
||||||
|
chmod a+x ~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
|
||||||
|
```
|
||||||
|
|
||||||
|
3. You can double-click on the AppImage to run it, or in a terminal with this command:
|
||||||
|
```bash
|
||||||
|
~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
|
||||||
|
```
|
@ -99,7 +99,7 @@ yarn tron:start
|
|||||||
|
|
||||||
This will start the application and hot-reload on changes.
|
This will start the application and hot-reload on changes.
|
||||||
|
|
||||||
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
|
||||||
|
|
||||||
To build, run `yarn tron:package`.
|
To build, run `yarn tron:package`.
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ to other modules.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment = (x) => {
|
export fn increment(x) {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -37,11 +37,11 @@ Multiple functions can be exported in a file.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment = (x) => {
|
export fn increment(x) {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn decrement = (x) => {
|
export fn decrement(x) {
|
||||||
return x - 1
|
return x - 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
20904
docs/kcl/std.json
@ -54,7 +54,7 @@ We also have support for defining your own functions. Functions can take in any
|
|||||||
type of argument. Below is an example of the syntax:
|
type of argument. Below is an example of the syntax:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn myFn = (x) => {
|
fn myFn(x) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -118,7 +118,7 @@ use the tag `rectangleSegmentA001` in any function or expression in the file.
|
|||||||
However if the code was written like this:
|
However if the code was written like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect = (origin) => {
|
fn rect(origin) {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
@ -146,7 +146,7 @@ Tags are accessible through the sketch group they are declared in.
|
|||||||
For example the following code works.
|
For example the following code works.
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect = (origin) => {
|
fn rect(origin) {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|
@ -1,161 +0,0 @@
|
|||||||
---
|
|
||||||
title: "BinaryOperator"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
Add two numbers.
|
|
||||||
|
|
||||||
**enum:** `+`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Subtract two numbers.
|
|
||||||
|
|
||||||
**enum:** `-`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Multiply two numbers.
|
|
||||||
|
|
||||||
**enum:** `*`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Divide two numbers.
|
|
||||||
|
|
||||||
**enum:** `/`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Modulo two numbers.
|
|
||||||
|
|
||||||
**enum:** `%`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Raise a number to a power.
|
|
||||||
|
|
||||||
**enum:** `^`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Are two numbers equal?
|
|
||||||
|
|
||||||
**enum:** `==`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Are two numbers not equal?
|
|
||||||
|
|
||||||
**enum:** `!=`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Is left greater than right
|
|
||||||
|
|
||||||
**enum:** `>`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Is left greater than or equal to right
|
|
||||||
|
|
||||||
**enum:** `>=`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Is left less than right
|
|
||||||
|
|
||||||
**enum:** `<`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Is left less than or equal to right
|
|
||||||
|
|
||||||
**enum:** `<=`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
|||||||
---
|
|
||||||
title: "BinaryPart"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Literal`| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
|
||||||
| `raw` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `BinaryExpression`| | No |
|
|
||||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
|
|
||||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
|
||||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `CallExpression`| | No |
|
|
||||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `UnaryExpression`| | No |
|
|
||||||
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No |
|
|
||||||
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
|
||||||
| `computed` |`boolean`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `IfExpression`| | No |
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
|
||||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
|
||||||
| `final_else` |[`Program`](/docs/kcl/types/Program)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
|||||||
---
|
|
||||||
title: "BodyItem"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ImportStatement`| | No |
|
|
||||||
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
|
||||||
| `path` |`string`| | No |
|
|
||||||
| `raw_path` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ExpressionStatement`| | No |
|
|
||||||
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `VariableDeclaration`| | No |
|
|
||||||
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
|
||||||
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
|
||||||
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ReturnStatement`| | No |
|
|
||||||
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
title: "CommentStyle"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
Like // foo
|
|
||||||
|
|
||||||
**enum:** `line`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Like /* foo */
|
|
||||||
|
|
||||||
**enum:** `block`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ElseIf"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
title: "EnvironmentRef"
|
|
||||||
excerpt: "An index pointing to an environment."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
An index pointing to an environment.
|
|
||||||
|
|
||||||
**Type:** `integer` (`uint`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,317 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Expr"
|
|
||||||
excerpt: "An expression can be evaluated to yield a single KCL value."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
An expression can be evaluated to yield a single KCL value.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Literal`| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `raw` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
|
||||||
| `value` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `BinaryExpression`| | No |
|
|
||||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
|
||||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
|
||||||
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `CallExpression`| | No |
|
|
||||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `PipeExpression`| | No |
|
|
||||||
| `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `PipeSubstitution`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ArrayExpression`| | No |
|
|
||||||
| `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ArrayRangeExpression`| | No |
|
|
||||||
| `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `endInclusive` |`boolean`| Is the `end_element` included in the range? | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ObjectExpression`| | No |
|
|
||||||
| `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No |
|
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `computed` |`boolean`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `UnaryExpression`| | No |
|
|
||||||
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `IfExpression`| | No |
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
|
||||||
| `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `None`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "FunctionExpression"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
|
||||||
| `body` |[`Program`](/docs/kcl/types/Program)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Identifier"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ImportItem"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
|
||||||
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ItemVisibility"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**enum:** `default`, `export`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -317,7 +317,6 @@ Data for an imported geometry.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Function`| | No |
|
| `type` |enum: `Function`| | No |
|
||||||
| `expression` |[`FunctionExpression`](/docs/kcl/types/FunctionExpression)| Any KCL value. | No |
|
|
||||||
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
---
|
|
||||||
title: "LiteralIdentifier"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Literal`| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
|
||||||
| `raw` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
title: "LiteralValue"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts any of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `number` (`double`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `string`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `boolean`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
title: "MemberObject"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
|
||||||
| `computed` |`boolean`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
title: "NonCodeMeta"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `nonCodeNodes` |`object`| | No |
|
|
||||||
| `startNodes` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "NonCodeNode"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
|||||||
---
|
|
||||||
title: "NonCodeValue"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `inlineComment`| | No |
|
|
||||||
| `value` |`string`| | No |
|
|
||||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
A block comment. An example of this is the following: ```python,no_run /* This is a block comment */ 1 + 1 ``` Now this is important. The block comment is attached to the next line. This is always the case. Also the block comment doesn't have a new line above it. If it did it would be a `NewLineBlockComment`.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `blockComment`| | No |
|
|
||||||
| `value` |`string`| | No |
|
|
||||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
A block comment that has a new line above it. The user explicitly added a new line above the block comment.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `newLineBlockComment`| | No |
|
|
||||||
| `value` |`string`| | No |
|
|
||||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `newLine`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ObjectProperty"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Parameter"
|
|
||||||
excerpt: "Parameter of a KCL function."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Parameter of a KCL function.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `identifier` |[`Identifier`](/docs/kcl/types/Identifier)| The parameter's label or name. | No |
|
|
||||||
| `optional` |`boolean`| Is the parameter optional? | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Program"
|
|
||||||
excerpt: "A KCL program top level, or function body."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A KCL program top level, or function body.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
|
|
||||||
| `shebang` |[`Shebang`](/docs/kcl/types/Shebang)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Shebang"
|
|
||||||
excerpt: "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```"
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `content` |`string`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Uint"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `integer` (`uint32`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
title: "UnaryOperator"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
Negate a number.
|
|
||||||
|
|
||||||
**enum:** `-`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Negate a boolean.
|
|
||||||
|
|
||||||
**enum:** `!`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "VariableDeclarator"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
|
|
||||||
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
title: "VariableKind"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
Declare a named constant.
|
|
||||||
|
|
||||||
**enum:** `const`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Declare a function.
|
|
||||||
|
|
||||||
**enum:** `fn`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -458,8 +458,8 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
/* add the following code to the editor ($ error is not a valid line)
|
/* add the following code to the editor ($ error is not a valid line)
|
||||||
$ error
|
$ error
|
||||||
const topAng = 30
|
topAng = 30
|
||||||
const bottomAng = 25
|
bottomAng = 25
|
||||||
*/
|
*/
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await page.keyboard.type('$ error')
|
await page.keyboard.type('$ error')
|
||||||
@ -474,12 +474,14 @@ test.describe('Editor tests', () => {
|
|||||||
await page.keyboard.type('bottomAng = 25')
|
await page.keyboard.type('bottomAng = 25')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// error in guter
|
// error in gutter
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token: $').first()).toBeVisible()
|
await expect(
|
||||||
|
page.getByText('Tag names must not be empty').first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
// select the line that's causing the error and delete it
|
// select the line that's causing the error and delete it
|
||||||
await page.getByText('$ error').click()
|
await page.getByText('$ error').click()
|
||||||
@ -518,7 +520,10 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
|
// TODO currently multiple source ranges are not supported
|
||||||
|
test.skip('error with 2 source ranges gets 2 diagnostics', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -45,7 +45,6 @@ test.describe('integrations tests', () => {
|
|||||||
{
|
{
|
||||||
title: 'test-sample',
|
title: 'test-sample',
|
||||||
fileCount: 1,
|
fileCount: 1,
|
||||||
folderCount: 1,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
@ -233,7 +232,6 @@ test.describe('when using the file tree to', () => {
|
|||||||
{
|
{
|
||||||
title: projectName,
|
title: projectName,
|
||||||
fileCount: 2,
|
fileCount: 2,
|
||||||
folderCount: 2, // TODO: This is a pre-existing bug, there are no folders within the project
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
|
@ -4,7 +4,6 @@ import { expect } from '@playwright/test'
|
|||||||
interface ProjectCardState {
|
interface ProjectCardState {
|
||||||
title: string
|
title: string
|
||||||
fileCount: number
|
fileCount: number
|
||||||
folderCount: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HomePageState {
|
interface HomePageState {
|
||||||
@ -61,15 +60,13 @@ export class HomePageFixture {
|
|||||||
const projectCards = await this.projectCard.all()
|
const projectCards = await this.projectCard.all()
|
||||||
const projectCardStates: Array<ProjectCardState> = []
|
const projectCardStates: Array<ProjectCardState> = []
|
||||||
for (const projectCard of projectCards) {
|
for (const projectCard of projectCards) {
|
||||||
const [title, fileCount, folderCount] = await Promise.all([
|
const [title, fileCount] = await Promise.all([
|
||||||
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
||||||
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
||||||
Number(await projectCard.locator(this.projectCardFolder).textContent()),
|
|
||||||
])
|
])
|
||||||
projectCardStates.push({
|
projectCardStates.push({
|
||||||
title: title,
|
title: title,
|
||||||
fileCount,
|
fileCount,
|
||||||
folderCount,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return projectCardStates
|
return projectCardStates
|
||||||
|
@ -6,6 +6,8 @@ export class ToolbarFixture {
|
|||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
|
loftButton!: Locator
|
||||||
|
shellButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
@ -26,6 +28,8 @@ export class ToolbarFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
|
this.loftButton = page.getByTestId('loft')
|
||||||
|
this.shellButton = page.getByTestId('shell')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
|
@ -677,3 +677,257 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const loftPointAndClickCases = [
|
||||||
|
{ shouldPreselect: true },
|
||||||
|
{ shouldPreselect: false },
|
||||||
|
]
|
||||||
|
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||||
|
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
||||||
|
app,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 50)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 575, y: 200 }
|
||||||
|
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnSketch2] = scene.makeMouseHelpers(
|
||||||
|
testPoint.x,
|
||||||
|
testPoint.y + 80
|
||||||
|
)
|
||||||
|
const loftDeclaration = 'loft001 = loft([sketch001, sketch002])'
|
||||||
|
|
||||||
|
await test.step(`Look for the white of the sketch001 shape`, async () => {
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function selectSketches() {
|
||||||
|
await clickOnSketch1()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickOnSketch2()
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldPreselect) {
|
||||||
|
await test.step(`Go through the command bar flow without preselected sketches`, async () => {
|
||||||
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: { Selection: '' },
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await selectSketches()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Selection: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await test.step(`Preselect the two sketches`, async () => {
|
||||||
|
await selectSketches()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
||||||
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Selection: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(loftDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [loftDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const shellPointAndClickCapCases = [
|
||||||
|
{ shouldPreselect: true },
|
||||||
|
{ shouldPreselect: false },
|
||||||
|
]
|
||||||
|
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
||||||
|
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
|
||||||
|
app,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
extrude001 = extrude(30, sketch001)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 575, y: 200 }
|
||||||
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const shellDeclaration =
|
||||||
|
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
|
||||||
|
|
||||||
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
|
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!shouldPreselect) {
|
||||||
|
await test.step(`Go through the command bar flow without preselected faces`, async () => {
|
||||||
|
await toolbar.shellButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '',
|
||||||
|
Thickness: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await clickOnCap()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 cap',
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await test.step(`Preselect the cap`, async () => {
|
||||||
|
await clickOnCap()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => {
|
||||||
|
await toolbar.shellButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 cap',
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(shellDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [shellDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await scene.expectPixelColor([146, 146, 146], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shell point-and-click wall', async ({
|
||||||
|
app,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-20, 20], %)
|
||||||
|
|> xLine(40, %)
|
||||||
|
|> yLine(-60, %)
|
||||||
|
|> xLine(-40, %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(40, sketch001)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 580, y: 180 }
|
||||||
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
||||||
|
const mutatedCode = 'xLine(-40, %, $seg01)'
|
||||||
|
const shellDeclaration =
|
||||||
|
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
|
||||||
|
const formattedOutLastLine = '}, extrude001)'
|
||||||
|
|
||||||
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
|
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => {
|
||||||
|
await toolbar.shellButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '',
|
||||||
|
Thickness: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await clickOnCap()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickOnWall()
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 cap, 1 face',
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(mutatedCode)
|
||||||
|
await editor.expectEditor.toContain(shellDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [formattedOutLastLine],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -136,6 +136,335 @@ test(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
const errorDir = join(dir, 'broken-code')
|
||||||
|
await fsp.mkdir(errorDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('broken-code-test.kcl'),
|
||||||
|
join(errorDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
||||||
|
await page.getByTestId('app-logo').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
||||||
|
await expect(page.getByText('broken-code')).toBeVisible()
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
await expect(page.getByText('New Project')).toBeVisible()
|
||||||
|
})
|
||||||
|
await test.step('opening broken code project should clear the scene and show the error', async () => {
|
||||||
|
// Go back home.
|
||||||
|
await expect(page.getByText('broken-code')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('broken-code').click()
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
const crypticErrorText = `Expected a tag declarator`
|
||||||
|
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||||
|
|
||||||
|
// black pixel means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
const emptyDir = join(dir, 'empty')
|
||||||
|
await fsp.mkdir(emptyDir, { recursive: true })
|
||||||
|
await fsp.writeFile(join(emptyDir, 'main.kcl'), '')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
||||||
|
await page.getByTestId('app-logo').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
||||||
|
await expect(page.getByText('empty')).toBeVisible()
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
await expect(page.getByText('New Project')).toBeVisible()
|
||||||
|
})
|
||||||
|
await test.step('opening empty code project should clear the scene', async () => {
|
||||||
|
// Go back home.
|
||||||
|
await expect(page.getByText('empty')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('empty').click()
|
||||||
|
|
||||||
|
// Ensure the code is empty.
|
||||||
|
await expect(u.codeLocator).toContainText('')
|
||||||
|
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
|
||||||
|
|
||||||
|
// planes colors means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open empty file, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
|
||||||
|
await fsp.writeFile(join(bracketDir, 'empty.kcl'), '')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
await test.step('creating a empty file should clear the scene', async () => {
|
||||||
|
// open the file pane.
|
||||||
|
await page.getByTestId('files-pane-button').click()
|
||||||
|
|
||||||
|
// OPen the other file.
|
||||||
|
const file = page.getByRole('button', { name: 'empty.kcl' })
|
||||||
|
await expect(file).toBeVisible()
|
||||||
|
|
||||||
|
await file.click()
|
||||||
|
|
||||||
|
// planes colors means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
|
||||||
|
// Ensure the code is empty.
|
||||||
|
await expect(u.codeLocator).toContainText('')
|
||||||
|
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('broken-code-test.kcl'),
|
||||||
|
join(bracketDir, 'broken-code-test.kcl')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
await test.step('opening broken code file should clear the scene and show the error', async () => {
|
||||||
|
// open the file pane.
|
||||||
|
await page.getByTestId('files-pane-button').click()
|
||||||
|
|
||||||
|
// OPen the other file.
|
||||||
|
const file = page.getByRole('button', { name: 'broken-code-test.kcl' })
|
||||||
|
await expect(file).toBeVisible()
|
||||||
|
|
||||||
|
await file.click()
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
const crypticErrorText = `Expected a tag declarator`
|
||||||
|
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||||
|
|
||||||
|
// black pixel means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'when code with error first loads you get errors in console',
|
'when code with error first loads you get errors in console',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
|
@ -550,7 +550,7 @@ sketch001 = startSketchAt([-0, -0])
|
|||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Constants and locators
|
// Constants and locators
|
||||||
const planeColor: [number, number, number] = [170, 220, 170]
|
const planeColor: [number, number, number] = [161, 220, 155]
|
||||||
const bgColor: [number, number, number] = [27, 27, 27]
|
const bgColor: [number, number, number] = [27, 27, 27]
|
||||||
const middlePixelIsColor = async (color: [number, number, number]) => {
|
const middlePixelIsColor = async (color: [number, number, number]) => {
|
||||||
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
||||||
|
@ -7,6 +7,8 @@ try {
|
|||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((line) => line && line.length > 1)
|
.filter((line) => line && line.length > 1)
|
||||||
.forEach((line) => {
|
.forEach((line) => {
|
||||||
|
// Allow line comments.
|
||||||
|
if (line.trimStart().startsWith('#')) return
|
||||||
const [key, value] = line.split('=')
|
const [key, value] = line.split('=')
|
||||||
// prefer env vars over secrets file
|
// prefer env vars over secrets file
|
||||||
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
||||||
|
@ -943,6 +943,110 @@ sketch002 = startSketchOn(extrude001, 'END')
|
|||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* TODO: once we fix bug turn on.
|
||||||
|
test('empty-scene default-planes act as expected when spaces in file', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
// Fill with spaces
|
||||||
|
await u.codeLocator.fill(`
|
||||||
|
`)
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('empty-scene default-planes act as expected when only code comments in file', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
// Fill with spaces
|
||||||
|
await u.codeLocator.fill(`// this is a code comments ya nerds
|
||||||
|
`)
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
})*/
|
||||||
|
|
||||||
test('empty-scene default-planes act as expected', async ({
|
test('empty-scene default-planes act as expected', async ({
|
||||||
page,
|
page,
|
||||||
browserName,
|
browserName,
|
||||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
@ -23,7 +23,7 @@ test.describe('Test toggling perspective', () => {
|
|||||||
y: screenHeight * 0.4,
|
y: screenHeight * 0.4,
|
||||||
}
|
}
|
||||||
const backgroundColor: [number, number, number] = [29, 29, 29]
|
const backgroundColor: [number, number, number] = [29, 29, 29]
|
||||||
const xzPlaneColor: [number, number, number] = [50, 50, 99]
|
const xzPlaneColor: [number, number, number] = [82, 55, 96]
|
||||||
const locationToHaveColor = async (color: [number, number, number]) => {
|
const locationToHaveColor = async (color: [number, number, number]) => {
|
||||||
return u.getGreatestPixDiff(checkedScreenLocation, color)
|
return u.getGreatestPixDiff(checkedScreenLocation, color)
|
||||||
}
|
}
|
||||||
|
11
package.json
@ -81,6 +81,7 @@
|
|||||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
|
"simpleserver:stop": "kill-port 3000",
|
||||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
||||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
||||||
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
||||||
@ -95,6 +96,8 @@
|
|||||||
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
||||||
"files:set-notes": "./scripts/set-files-notes.sh",
|
"files:set-notes": "./scripts/set-files-notes.sh",
|
||||||
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
||||||
|
"files:invalidate-bucket": "./scripts/invalidate-files-bucket.sh",
|
||||||
|
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
|
||||||
"postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
|
"postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
|
||||||
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
|
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
|
||||||
"make:dev": "make dev",
|
"make:dev": "make dev",
|
||||||
@ -158,6 +161,7 @@
|
|||||||
"@electron/rebuild": "^3.6.0",
|
"@electron/rebuild": "^3.6.0",
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"@lezer/generator": "^1.7.1",
|
"@lezer/generator": "^1.7.1",
|
||||||
|
"@nabla/vite-plugin-eslint": "^2.0.5",
|
||||||
"@playwright/test": "^1.46.1",
|
"@playwright/test": "^1.46.1",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^15.0.2",
|
"@testing-library/react": "^15.0.2",
|
||||||
@ -170,7 +174,7 @@
|
|||||||
"@types/pixelmatch": "^5.2.6",
|
"@types/pixelmatch": "^5.2.6",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/react": "^18.3.4",
|
"@types/react": "^18.3.4",
|
||||||
"@types/react-dom": "^18.2.25",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
"@types/three": "^0.163.0",
|
"@types/three": "^0.163.0",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
@ -192,7 +196,7 @@
|
|||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"eslint-plugin-import": "^2.30.0",
|
"eslint-plugin-import": "^2.30.0",
|
||||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||||
"happy-dom": "^15.10.2",
|
"happy-dom": "^15.11.7",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"husky": "^9.1.5",
|
"husky": "^9.1.5",
|
||||||
"kill-port": "^2.0.1",
|
"kill-port": "^2.0.1",
|
||||||
@ -207,12 +211,11 @@
|
|||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"vite": "^5.4.6",
|
"vite": "^5.4.6",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vitest": "^1.6.0",
|
"vitest": "^1.6.0",
|
||||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||||
"wasm-pack": "^0.13.0",
|
"wasm-pack": "^0.13.1",
|
||||||
"ws": "^8.17.0",
|
"ws": "^8.17.0",
|
||||||
"yarn": "^1.22.22"
|
"yarn": "^1.22.22"
|
||||||
}
|
}
|
||||||
|
5
scripts/get-nightly-changelog.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "## What's Changed"
|
||||||
|
git log ${PREVIOUS_TAG}..HEAD --oneline --pretty=format:%s | grep -v Bump | awk '{print "* "toupper(substr($0,0,1))substr($0,2)}'
|
||||||
|
echo ""
|
||||||
|
echo "**Full Changelog**: https://github.com/KittyCAD/modeling-app/compare/${PREVIOUS_TAG}...${TAG}"
|
11
scripts/invalidate-files-bucket.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
base_dir="/releases/modeling-app"
|
||||||
|
if [[ $1 = "--nightly" ]]; then
|
||||||
|
base_dir="/releases/modeling-app/nightly"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Invalidating json and yml files at $base_dir in the download bucket"
|
||||||
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/last_download.json" --async
|
||||||
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest-linux-arm64.yml" --async
|
||||||
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest-mac.yml" --async
|
||||||
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest.yml" --async
|
@ -155,7 +155,6 @@ export class CameraControls {
|
|||||||
this.camera.zoom = camProps.zoom || 1
|
this.camera.zoom = camProps.zoom || 1
|
||||||
}
|
}
|
||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
console.log('doing this thing', camProps)
|
|
||||||
this.update(true)
|
this.update(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,14 +272,26 @@ export class CameraControls {
|
|||||||
camSettings.center.y,
|
camSettings.center.y,
|
||||||
camSettings.center.z
|
camSettings.center.z
|
||||||
)
|
)
|
||||||
const quat = new Quaternion(
|
const orientation = new Quaternion(
|
||||||
camSettings.orientation.x,
|
camSettings.orientation.x,
|
||||||
camSettings.orientation.y,
|
camSettings.orientation.y,
|
||||||
camSettings.orientation.z,
|
camSettings.orientation.z,
|
||||||
camSettings.orientation.w
|
camSettings.orientation.w
|
||||||
).invert()
|
).invert()
|
||||||
|
|
||||||
this.camera.up.copy(new Vector3(0, 1, 0).applyQuaternion(quat))
|
const newUp = new Vector3(
|
||||||
|
camSettings.up.x,
|
||||||
|
camSettings.up.y,
|
||||||
|
camSettings.up.z
|
||||||
|
)
|
||||||
|
this.camera.quaternion.set(
|
||||||
|
orientation.x,
|
||||||
|
orientation.y,
|
||||||
|
orientation.z,
|
||||||
|
orientation.w
|
||||||
|
)
|
||||||
|
this.camera.up.copy(newUp)
|
||||||
|
this.camera.updateProjectionMatrix()
|
||||||
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
||||||
this.useOrthographicCamera()
|
this.useOrthographicCamera()
|
||||||
}
|
}
|
||||||
@ -1164,7 +1175,7 @@ export class CameraControls {
|
|||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.syncDirection === 'clientToEngine' || forceUpdate)
|
if (this.syncDirection === 'clientToEngine' || forceUpdate) {
|
||||||
this.throttledUpdateEngineCamera({
|
this.throttledUpdateEngineCamera({
|
||||||
quaternion: this.camera.quaternion,
|
quaternion: this.camera.quaternion,
|
||||||
position: this.camera.position,
|
position: this.camera.position,
|
||||||
@ -1172,6 +1183,7 @@ export class CameraControls {
|
|||||||
isPerspective: this.isPerspective,
|
isPerspective: this.isPerspective,
|
||||||
target: this.target,
|
target: this.target,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
this.deferReactUpdate(this.reactCameraProperties)
|
this.deferReactUpdate(this.reactCameraProperties)
|
||||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ import {
|
|||||||
Expr,
|
Expr,
|
||||||
parse,
|
parse,
|
||||||
recast,
|
recast,
|
||||||
|
defaultSourceRange,
|
||||||
|
resultIsOk,
|
||||||
|
ProgramMemory,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
||||||
import { ConstrainInfo } from 'lang/std/stdTypes'
|
import { ConstrainInfo } from 'lang/std/stdTypes'
|
||||||
@ -412,14 +415,15 @@ export async function deleteSegment({
|
|||||||
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
|
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
modifiedAst = parse(newCode)
|
const pResult = parse(newCode)
|
||||||
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
if (err(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
|
modifiedAst = pResult.program
|
||||||
|
|
||||||
const testExecute = await executeAst({
|
const testExecute = await executeAst({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: engineCommandManager,
|
engineCommandManager: engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
if (testExecute.errors.length) {
|
if (testExecute.errors.length) {
|
||||||
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
||||||
@ -590,7 +594,9 @@ const ConstraintSymbol = ({
|
|||||||
if (err(_node)) return
|
if (err(_node)) return
|
||||||
const node = _node.node
|
const node = _node.node
|
||||||
|
|
||||||
const range: SourceRange = node ? [node.start, node.end] : [0, 0]
|
const range: SourceRange = node
|
||||||
|
? [node.start, node.end, true]
|
||||||
|
: defaultSourceRange()
|
||||||
|
|
||||||
if (_type === 'intersectionTag') return null
|
if (_type === 'intersectionTag') return null
|
||||||
|
|
||||||
@ -612,7 +618,7 @@ const ConstraintSymbol = ({
|
|||||||
editorManager.setHighlightRange([range])
|
editorManager.setHighlightRange([range])
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
}}
|
}}
|
||||||
// disabled={isConstrained || !convertToVarEnabled}
|
// disabled={isConstrained || !convertToVarEnabled}
|
||||||
// disabled={implicitDesc} TODO why does this change styles that are hard to override?
|
// disabled={implicitDesc} TODO why does this change styles that are hard to override?
|
||||||
@ -627,10 +633,12 @@ const ConstraintSymbol = ({
|
|||||||
})
|
})
|
||||||
} else if (isConstrained) {
|
} else if (isConstrained) {
|
||||||
try {
|
try {
|
||||||
const parsed = parse(recast(kclManager.ast))
|
const pResult = parse(recast(kclManager.ast))
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(pResult)
|
||||||
|
|
||||||
const _node1 = getNodeFromPath<CallExpression>(
|
const _node1 = getNodeFromPath<CallExpression>(
|
||||||
parsed,
|
pResult.program!,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression',
|
'CallExpression',
|
||||||
true
|
true
|
||||||
|
@ -48,6 +48,9 @@ import {
|
|||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
sketchFromKclValueOptional,
|
sketchFromKclValueOptional,
|
||||||
|
defaultSourceRange,
|
||||||
|
sourceRangeFromRust,
|
||||||
|
resultIsOk,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
@ -495,10 +498,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
const sketch = sketchFromPathToNode({
|
const sketch = sketchFromPathToNode({
|
||||||
@ -530,7 +532,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
sketch.start.__geoMeta.sourceRange
|
sourceRangeFromRust(sketch.start.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
if (sketch?.paths?.[0]?.type !== 'Circle') {
|
if (sketch?.paths?.[0]?.type !== 'Circle') {
|
||||||
const _profileStart = createProfileStartHandle({
|
const _profileStart = createProfileStartHandle({
|
||||||
@ -552,7 +554,7 @@ export class SceneEntities {
|
|||||||
sketch.paths.forEach((segment, index) => {
|
sketch.paths.forEach((segment, index) => {
|
||||||
let segPathToNode = getNodePathFromSourceRange(
|
let segPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
segment.__geoMeta.sourceRange
|
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
draftExpressionsIndices &&
|
draftExpressionsIndices &&
|
||||||
@ -561,12 +563,12 @@ export class SceneEntities {
|
|||||||
const previousSegment = sketch.paths[index - 1] || sketch.start
|
const previousSegment = sketch.paths[index - 1] || sketch.start
|
||||||
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
previousSegment.__geoMeta.sourceRange
|
sourceRangeFromRust(previousSegment.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
const bodyIndex = previousSegmentPathToNode[1][0]
|
const bodyIndex = previousSegmentPathToNode[1][0]
|
||||||
segPathToNode = getNodePathFromSourceRange(
|
segPathToNode = getNodePathFromSourceRange(
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
segment.__geoMeta.sourceRange
|
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
segPathToNode[1][0] = bodyIndex
|
segPathToNode[1][0] = bodyIndex
|
||||||
}
|
}
|
||||||
@ -575,7 +577,10 @@ export class SceneEntities {
|
|||||||
index <= draftExpressionsIndices.end &&
|
index <= draftExpressionsIndices.end &&
|
||||||
index >= draftExpressionsIndices.start
|
index >= draftExpressionsIndices.start
|
||||||
const isSelected = selectionRanges?.graphSelections.some((selection) =>
|
const isSelected = selectionRanges?.graphSelections.some((selection) =>
|
||||||
isOverlap(selection?.codeRef?.range, segment.__geoMeta.sourceRange)
|
isOverlap(
|
||||||
|
selection?.codeRef?.range,
|
||||||
|
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
let seg: Group
|
let seg: Group
|
||||||
@ -657,13 +662,11 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
updateAstAndRejigSketch = async (
|
updateAstAndRejigSketch = async (
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
modifiedAst: Node<Program> | Error,
|
modifiedAst: Node<Program>,
|
||||||
forward: [number, number, number],
|
forward: [number, number, number],
|
||||||
up: [number, number, number],
|
up: [number, number, number],
|
||||||
origin: [number, number, number]
|
origin: [number, number, number]
|
||||||
) => {
|
) => {
|
||||||
if (err(modifiedAst)) return modifiedAst
|
|
||||||
|
|
||||||
const nextAst = await kclManager.updateAst(modifiedAst, false)
|
const nextAst = await kclManager.updateAst(modifiedAst, false)
|
||||||
await this.tearDownSketch({ removeAxis: false })
|
await this.tearDownSketch({ removeAxis: false })
|
||||||
sceneInfra.resetMouseListeners()
|
sceneInfra.resetMouseListeners()
|
||||||
@ -698,8 +701,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
const variableDeclarationName =
|
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
||||||
_node1.node?.declarations?.[0]?.id?.name || ''
|
|
||||||
|
|
||||||
const sg = sketchFromKclValue(
|
const sg = sketchFromKclValue(
|
||||||
kclManager.programMemory.get(variableDeclarationName),
|
kclManager.programMemory.get(variableDeclarationName),
|
||||||
@ -721,8 +723,9 @@ export class SceneEntities {
|
|||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
})
|
})
|
||||||
if (trap(mod)) return Promise.reject(mod)
|
if (trap(mod)) return Promise.reject(mod)
|
||||||
const modifiedAst = parse(recast(mod.modifiedAst))
|
const pResult = parse(recast(mod.modifiedAst))
|
||||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
|
const modifiedAst = pResult.program
|
||||||
|
|
||||||
const draftExpressionsIndices = { start: index, end: index }
|
const draftExpressionsIndices = { start: index, end: index }
|
||||||
|
|
||||||
@ -898,10 +901,9 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
const variableDeclarationName =
|
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
||||||
_node1.node?.declarations?.[0]?.id?.name || ''
|
const startSketchOn = _node1.node?.declaration
|
||||||
const startSketchOn = _node1.node?.declarations
|
const startSketchOnInit = startSketchOn?.init
|
||||||
const startSketchOnInit = startSketchOn?.[0]?.init
|
|
||||||
|
|
||||||
const tags: [string, string, string] = [
|
const tags: [string, string, string] = [
|
||||||
findUniqueName(_ast, 'rectangleSegmentA'),
|
findUniqueName(_ast, 'rectangleSegmentA'),
|
||||||
@ -909,14 +911,14 @@ export class SceneEntities {
|
|||||||
findUniqueName(_ast, 'rectangleSegmentC'),
|
findUniqueName(_ast, 'rectangleSegmentC'),
|
||||||
]
|
]
|
||||||
|
|
||||||
startSketchOn[0].init = createPipeExpression([
|
startSketchOn.init = createPipeExpression([
|
||||||
startSketchOnInit,
|
startSketchOnInit,
|
||||||
...getRectangleCallExpressions(rectangleOrigin, tags),
|
...getRectangleCallExpressions(rectangleOrigin, tags),
|
||||||
])
|
])
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
const pResult = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
_ast = _recastAst
|
_ast = pResult.program
|
||||||
|
|
||||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
@ -939,7 +941,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return Promise.reject(_node)
|
if (trap(_node)) return Promise.reject(_node)
|
||||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
const sketchInit = _node.node?.declaration.init
|
||||||
|
|
||||||
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
|
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
|
||||||
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
|
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
|
||||||
@ -950,10 +952,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -989,7 +990,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
const sketchInit = _node.node?.declaration.init
|
||||||
|
|
||||||
if (sketchInit.type !== 'PipeExpression') {
|
if (sketchInit.type !== 'PipeExpression') {
|
||||||
return
|
return
|
||||||
@ -998,9 +999,10 @@ export class SceneEntities {
|
|||||||
updateRectangleSketch(sketchInit, x, y, tags[0])
|
updateRectangleSketch(sketchInit, x, y, tags[0])
|
||||||
|
|
||||||
const newCode = recast(_ast)
|
const newCode = recast(_ast)
|
||||||
let _recastAst = parse(newCode)
|
const pResult = parse(newCode)
|
||||||
if (trap(_recastAst)) return
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
_ast = _recastAst
|
return Promise.reject(pResult)
|
||||||
|
_ast = pResult.program
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1013,10 +1015,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
|
|
||||||
@ -1055,10 +1056,9 @@ export class SceneEntities {
|
|||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
|
|
||||||
// startSketchOn already exists
|
// startSketchOn already exists
|
||||||
const variableDeclarationName =
|
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
||||||
_node1.node?.declarations?.[0]?.id?.name || ''
|
const startSketchOn = _node1.node?.declaration
|
||||||
const startSketchOn = _node1.node?.declarations
|
const startSketchOnInit = startSketchOn?.init
|
||||||
const startSketchOnInit = startSketchOn?.[0]?.init
|
|
||||||
|
|
||||||
const tags: [string, string, string] = [
|
const tags: [string, string, string] = [
|
||||||
findUniqueName(_ast, 'rectangleSegmentA'),
|
findUniqueName(_ast, 'rectangleSegmentA'),
|
||||||
@ -1066,14 +1066,14 @@ export class SceneEntities {
|
|||||||
findUniqueName(_ast, 'rectangleSegmentC'),
|
findUniqueName(_ast, 'rectangleSegmentC'),
|
||||||
]
|
]
|
||||||
|
|
||||||
startSketchOn[0].init = createPipeExpression([
|
startSketchOn.init = createPipeExpression([
|
||||||
startSketchOnInit,
|
startSketchOnInit,
|
||||||
...getRectangleCallExpressions(rectangleOrigin, tags),
|
...getRectangleCallExpressions(rectangleOrigin, tags),
|
||||||
])
|
])
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
const pResult = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
_ast = _recastAst
|
_ast = pResult.program
|
||||||
|
|
||||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
@ -1096,7 +1096,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return Promise.reject(_node)
|
if (trap(_node)) return Promise.reject(_node)
|
||||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
const sketchInit = _node.node?.declaration.init
|
||||||
|
|
||||||
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
|
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
|
||||||
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
|
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
|
||||||
@ -1114,10 +1114,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1153,7 +1152,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
const sketchInit = _node.node?.declaration.init
|
||||||
|
|
||||||
if (sketchInit.type === 'PipeExpression') {
|
if (sketchInit.type === 'PipeExpression') {
|
||||||
updateCenterRectangleSketch(
|
updateCenterRectangleSketch(
|
||||||
@ -1165,9 +1164,10 @@ export class SceneEntities {
|
|||||||
rectangleOrigin[1]
|
rectangleOrigin[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
const pResult = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
_ast = _recastAst
|
return Promise.reject(pResult)
|
||||||
|
_ast = pResult.program
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1180,10 +1180,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
|
|
||||||
@ -1222,12 +1221,11 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
const variableDeclarationName =
|
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
||||||
_node1.node?.declarations?.[0]?.id?.name || ''
|
const startSketchOn = _node1.node?.declaration
|
||||||
const startSketchOn = _node1.node?.declarations
|
const startSketchOnInit = startSketchOn?.init
|
||||||
const startSketchOnInit = startSketchOn?.[0]?.init
|
|
||||||
|
|
||||||
startSketchOn[0].init = createPipeExpression([
|
startSketchOn.init = createPipeExpression([
|
||||||
startSketchOnInit,
|
startSketchOnInit,
|
||||||
createCallExpressionStdLib('circle', [
|
createCallExpressionStdLib('circle', [
|
||||||
createObjectExpression({
|
createObjectExpression({
|
||||||
@ -1241,9 +1239,9 @@ export class SceneEntities {
|
|||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
const pResult = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
_ast = _recastAst
|
_ast = pResult.program
|
||||||
|
|
||||||
// do a quick mock execution to get the program memory up-to-date
|
// do a quick mock execution to get the program memory up-to-date
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1269,7 +1267,7 @@ export class SceneEntities {
|
|||||||
)
|
)
|
||||||
let modded = structuredClone(truncatedAst)
|
let modded = structuredClone(truncatedAst)
|
||||||
if (trap(_node)) return
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
const sketchInit = _node.node.declaration.init
|
||||||
|
|
||||||
const x = (args.intersectionPoint.twoD.x || 0) - circleCenter[0]
|
const x = (args.intersectionPoint.twoD.x || 0) - circleCenter[0]
|
||||||
const y = (args.intersectionPoint.twoD.y || 0) - circleCenter[1]
|
const y = (args.intersectionPoint.twoD.y || 0) - circleCenter[1]
|
||||||
@ -1299,10 +1297,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: modded,
|
ast: modded,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1338,7 +1335,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
const sketchInit = _node.node?.declaration.init
|
||||||
|
|
||||||
let modded = structuredClone(_ast)
|
let modded = structuredClone(_ast)
|
||||||
if (sketchInit.type === 'PipeExpression') {
|
if (sketchInit.type === 'PipeExpression') {
|
||||||
@ -1365,9 +1362,10 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const newCode = recast(modded)
|
const newCode = recast(modded)
|
||||||
if (err(newCode)) return
|
if (err(newCode)) return
|
||||||
let _recastAst = parse(newCode)
|
const pResult = parse(newCode)
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
_ast = _recastAst
|
return Promise.reject(pResult)
|
||||||
|
_ast = pResult.program
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1660,7 +1658,7 @@ export class SceneEntities {
|
|||||||
kclManager.programMemory,
|
kclManager.programMemory,
|
||||||
{
|
{
|
||||||
type: 'sourceRange',
|
type: 'sourceRange',
|
||||||
sourceRange: [node.start, node.end],
|
sourceRange: [node.start, node.end, true],
|
||||||
},
|
},
|
||||||
getChangeSketchInput()
|
getChangeSketchInput()
|
||||||
)
|
)
|
||||||
@ -1683,10 +1681,9 @@ export class SceneEntities {
|
|||||||
codeManager.updateCodeEditor(code)
|
codeManager.updateCodeEditor(code)
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1750,7 +1747,7 @@ export class SceneEntities {
|
|||||||
): (() => SegmentOverlayPayload | null) => {
|
): (() => SegmentOverlayPayload | null) => {
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
segment.__geoMeta.sourceRange
|
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
const sgPaths = sketch.paths
|
const sgPaths = sketch.paths
|
||||||
const originalPathToNodeStr = JSON.stringify(segPathToNode)
|
const originalPathToNodeStr = JSON.stringify(segPathToNode)
|
||||||
@ -1901,8 +1898,10 @@ export class SceneEntities {
|
|||||||
SEGMENT_BODIES_PLUS_PROFILE_START
|
SEGMENT_BODIES_PLUS_PROFILE_START
|
||||||
)
|
)
|
||||||
if (parent?.userData?.pathToNode) {
|
if (parent?.userData?.pathToNode) {
|
||||||
const updatedAst = parse(recast(kclManager.ast))
|
const pResult = parse(recast(kclManager.ast))
|
||||||
if (trap(updatedAst)) return
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(pResult)
|
||||||
|
const updatedAst = pResult.program
|
||||||
const _node = getNodeFromPath<Node<CallExpression>>(
|
const _node = getNodeFromPath<Node<CallExpression>>(
|
||||||
updatedAst,
|
updatedAst,
|
||||||
parent.userData.pathToNode,
|
parent.userData.pathToNode,
|
||||||
@ -1910,7 +1909,7 @@ export class SceneEntities {
|
|||||||
)
|
)
|
||||||
if (trap(_node, { suppress: true })) return
|
if (trap(_node, { suppress: true })) return
|
||||||
const node = _node.node
|
const node = _node.node
|
||||||
editorManager.setHighlightRange([[node.start, node.end]])
|
editorManager.setHighlightRange([[node.start, node.end, true]])
|
||||||
const yellow = 0xffff00
|
const yellow = 0xffff00
|
||||||
colorSegment(selected, yellow)
|
colorSegment(selected, yellow)
|
||||||
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||||
@ -1955,10 +1954,10 @@ export class SceneEntities {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
},
|
},
|
||||||
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
const parent = getParentGroup(
|
const parent = getParentGroup(
|
||||||
selected,
|
selected,
|
||||||
SEGMENT_BODIES_PLUS_PROFILE_START
|
SEGMENT_BODIES_PLUS_PROFILE_START
|
||||||
@ -2057,7 +2056,7 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (err(_node)) return _node
|
if (err(_node)) return _node
|
||||||
const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || ''
|
const variableDeclarationName = _node.node?.declaration.id?.name || ''
|
||||||
const sg = sketchFromKclValue(
|
const sg = sketchFromKclValue(
|
||||||
programMemory.get(variableDeclarationName),
|
programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
@ -2082,28 +2081,30 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
;(
|
;(
|
||||||
(_ast.body[bodyIndex] as VariableDeclaration).declarations[0]
|
(_ast.body[bodyIndex] as VariableDeclaration).declaration
|
||||||
.init as PipeExpression
|
.init as PipeExpression
|
||||||
).body.push(newSegment)
|
).body.push(newSegment)
|
||||||
// update source ranges to section we just added.
|
// update source ranges to section we just added.
|
||||||
// hacks like this wouldn't be needed if the AST put pathToNode info in memory/sketch segments
|
// hacks like this wouldn't be needed if the AST put pathToNode info in memory/sketch segments
|
||||||
const updatedSrcRangeAst = parse(recast(_ast)) // get source ranges correct since unfortunately we still rely on them
|
const pResult = parse(recast(_ast)) // get source ranges correct since unfortunately we still rely on them
|
||||||
if (err(updatedSrcRangeAst)) return updatedSrcRangeAst
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Error('Unexpected compilation error')
|
||||||
|
const updatedSrcRangeAst = pResult.program
|
||||||
|
|
||||||
const lastPipeItem = (
|
const lastPipeItem = (
|
||||||
(updatedSrcRangeAst.body[bodyIndex] as VariableDeclaration)
|
(updatedSrcRangeAst.body[bodyIndex] as VariableDeclaration).declaration
|
||||||
.declarations[0].init as PipeExpression
|
.init as PipeExpression
|
||||||
).body.slice(-1)[0]
|
).body.slice(-1)[0]
|
||||||
|
|
||||||
;(
|
;(
|
||||||
(_ast.body[bodyIndex] as VariableDeclaration).declarations[0]
|
(_ast.body[bodyIndex] as VariableDeclaration).declaration
|
||||||
.init as PipeExpression
|
.init as PipeExpression
|
||||||
).body.slice(-1)[0].start = lastPipeItem.start
|
).body.slice(-1)[0].start = lastPipeItem.start
|
||||||
|
|
||||||
_ast.end = lastPipeItem.end
|
_ast.end = lastPipeItem.end
|
||||||
const varDec = _ast.body[bodyIndex] as Node<VariableDeclaration>
|
const varDec = _ast.body[bodyIndex] as Node<VariableDeclaration>
|
||||||
varDec.end = lastPipeItem.end
|
varDec.end = lastPipeItem.end
|
||||||
const declarator = varDec.declarations[0]
|
const declarator = varDec.declaration
|
||||||
declarator.end = lastPipeItem.end
|
declarator.end = lastPipeItem.end
|
||||||
const init = declarator.init as Node<PipeExpression>
|
const init = declarator.init as Node<PipeExpression>
|
||||||
init.end = lastPipeItem.end
|
init.end = lastPipeItem.end
|
||||||
@ -2140,7 +2141,7 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
if (node.type !== 'VariableDeclaration') {
|
if (node.type !== 'VariableDeclaration') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const name = node.declarations[0].id.name
|
const name = node.declaration.id.name
|
||||||
const memoryItem = programMemory.get(name)
|
const memoryItem = programMemory.get(name)
|
||||||
if (!memoryItem) {
|
if (!memoryItem) {
|
||||||
continue
|
continue
|
||||||
|
@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { trap } from 'lib/trap'
|
import { trap } from 'lib/trap'
|
||||||
import { codeToIdSelections } from 'lib/selections'
|
import { codeToIdSelections } from 'lib/selections'
|
||||||
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
||||||
|
import { defaultSourceRange } from 'lang/wasm'
|
||||||
|
|
||||||
export function AstExplorer() {
|
export function AstExplorer() {
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
@ -46,7 +47,7 @@ export function AstExplorer() {
|
|||||||
<div
|
<div
|
||||||
className="h-full relative"
|
className="h-full relative"
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<pre className="text-xs">
|
<pre className="text-xs">
|
||||||
@ -115,15 +116,19 @@ function DisplayObj({
|
|||||||
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
||||||
}`}
|
}`}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
editorManager.setHighlightRange([[obj?.start || 0, obj.end]])
|
editorManager.setHighlightRange([[obj?.start || 0, obj.end, true]])
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}}
|
}}
|
||||||
onMouseMove={(e) => {
|
onMouseMove={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
editorManager.setHighlightRange([[obj?.start || 0, obj.end]])
|
editorManager.setHighlightRange([[obj?.start || 0, obj.end, true]])
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
const range: [number, number] = [obj?.start || 0, obj.end || 0]
|
const range: [number, number, boolean] = [
|
||||||
|
obj?.start || 0,
|
||||||
|
obj.end || 0,
|
||||||
|
true,
|
||||||
|
]
|
||||||
const idInfo = codeToIdSelections([
|
const idInfo = codeToIdSelections([
|
||||||
{ codeRef: codeRefFromRange(range, kclManager.ast) },
|
{ codeRef: codeRefFromRange(range, kclManager.ast) },
|
||||||
])[0]
|
])[0]
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { useEffect, useState, useRef } from 'react'
|
import { useEffect, useState, useRef } from 'react'
|
||||||
import { parse, BinaryPart, Expr, ProgramMemory } from '../lang/wasm'
|
import {
|
||||||
|
parse,
|
||||||
|
BinaryPart,
|
||||||
|
Expr,
|
||||||
|
ProgramMemory,
|
||||||
|
resultIsOk,
|
||||||
|
} from '../lang/wasm'
|
||||||
import {
|
import {
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
@ -141,8 +147,9 @@ export function useCalc({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const code = `const __result__ = ${value}`
|
const code = `const __result__ = ${value}`
|
||||||
const ast = parse(code)
|
const pResult = parse(code)
|
||||||
if (trap(ast)) return
|
if (trap(pResult) || !resultIsOk(pResult)) return
|
||||||
|
const ast = pResult.program
|
||||||
const _programMem: ProgramMemory = ProgramMemory.empty()
|
const _programMem: ProgramMemory = ProgramMemory.empty()
|
||||||
for (const { key, value } of availableVarInfo.variables) {
|
for (const { key, value } of availableVarInfo.variables) {
|
||||||
const error = _programMem.set(key, {
|
const error = _programMem.set(key, {
|
||||||
@ -156,18 +163,17 @@ export function useCalc({
|
|||||||
executeAst({
|
executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride: kclManager.programMemory.clone(),
|
programMemoryOverride: kclManager.programMemory.clone(),
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
}).then(({ execState }) => {
|
}).then(({ execState }) => {
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
a.type === 'VariableDeclaration' &&
|
a.type === 'VariableDeclaration' &&
|
||||||
a.declarations?.[0]?.id?.name === '__result__'
|
a.declaration.id?.name === '__result__'
|
||||||
)
|
)
|
||||||
const init =
|
const init =
|
||||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||||
resultDeclaration?.declarations?.[0]?.init
|
resultDeclaration?.declaration.init
|
||||||
const result = execState.memory?.get('__result__')?.value
|
const result = execState.memory?.get('__result__')?.value
|
||||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||||
init && setValueNode(init)
|
init && setValueNode(init)
|
||||||
|
@ -266,6 +266,7 @@ const FileTreeItem = ({
|
|||||||
// Let the lsp servers know we closed a file.
|
// Let the lsp servers know we closed a file.
|
||||||
onFileClose(currentFile?.path || null, project?.path || null)
|
onFileClose(currentFile?.path || null, project?.path || null)
|
||||||
onFileOpen(fileOrDir.path, project?.path || null)
|
onFileOpen(fileOrDir.path, project?.path || null)
|
||||||
|
kclManager.switchedFiles = true
|
||||||
|
|
||||||
// Open kcl files
|
// Open kcl files
|
||||||
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
||||||
|
@ -50,6 +50,8 @@ import {
|
|||||||
isSketchPipe,
|
isSketchPipe,
|
||||||
Selections,
|
Selections,
|
||||||
updateSelections,
|
updateSelections,
|
||||||
|
canLoftSelection,
|
||||||
|
canShellSelection,
|
||||||
} from 'lib/selections'
|
} from 'lib/selections'
|
||||||
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
||||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||||
@ -66,8 +68,9 @@ import {
|
|||||||
sketchOnOffsetPlane,
|
sketchOnOffsetPlane,
|
||||||
startSketchOnDefault,
|
startSketchOnDefault,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { Program, parse, recast } from 'lang/wasm'
|
import { Program, parse, recast, resultIsOk } from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
|
doesSceneHaveExtrudedSketch,
|
||||||
doesSceneHaveSweepableSketch,
|
doesSceneHaveSweepableSketch,
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
isSingleCursorInPipe,
|
isSingleCursorInPipe,
|
||||||
@ -82,7 +85,7 @@ import { getVarNameModal } from 'hooks/useToolbarGuards'
|
|||||||
import { err, reportRejection, trap } from 'lib/trap'
|
import { err, reportRejection, trap } from 'lib/trap'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { modelingMachineEvent } from 'editor/manager'
|
import { modelingMachineEvent } from 'editor/manager'
|
||||||
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addEdgeTreatment'
|
||||||
import {
|
import {
|
||||||
ExportIntent,
|
ExportIntent,
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
@ -569,6 +572,39 @@ export const ModelingMachineProvider = ({
|
|||||||
if (err(canSweep)) return false
|
if (err(canSweep)) return false
|
||||||
return canSweep
|
return canSweep
|
||||||
},
|
},
|
||||||
|
'has valid loft selection': ({ context: { selectionRanges } }) => {
|
||||||
|
const hasNoSelection =
|
||||||
|
selectionRanges.graphSelections.length === 0 ||
|
||||||
|
isRangeBetweenCharacters(selectionRanges) ||
|
||||||
|
isSelectionLastLine(selectionRanges, codeManager.code)
|
||||||
|
|
||||||
|
if (hasNoSelection) {
|
||||||
|
const count = 2
|
||||||
|
return doesSceneHaveSweepableSketch(kclManager.ast, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
const canLoft = canLoftSelection(selectionRanges)
|
||||||
|
if (err(canLoft)) return false
|
||||||
|
return canLoft
|
||||||
|
},
|
||||||
|
'has valid shell selection': ({
|
||||||
|
context: { selectionRanges },
|
||||||
|
event,
|
||||||
|
}) => {
|
||||||
|
const hasNoSelection =
|
||||||
|
selectionRanges.graphSelections.length === 0 ||
|
||||||
|
isRangeBetweenCharacters(selectionRanges) ||
|
||||||
|
isSelectionLastLine(selectionRanges, codeManager.code)
|
||||||
|
|
||||||
|
if (hasNoSelection) {
|
||||||
|
return doesSceneHaveExtrudedSketch(kclManager.ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
const canShell = canShellSelection(selectionRanges)
|
||||||
|
console.log('canShellSelection', canShellSelection(selectionRanges))
|
||||||
|
if (err(canShell)) return false
|
||||||
|
return canShell
|
||||||
|
},
|
||||||
'has valid selection for deletion': ({
|
'has valid selection for deletion': ({
|
||||||
context: { selectionRanges },
|
context: { selectionRanges },
|
||||||
}) => {
|
}) => {
|
||||||
@ -576,8 +612,10 @@ export const ModelingMachineProvider = ({
|
|||||||
if (selectionRanges.graphSelections.length <= 0) return false
|
if (selectionRanges.graphSelections.length <= 0) return false
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
'has valid fillet selection': ({ context: { selectionRanges } }) => {
|
'has valid edge treatment selection': ({
|
||||||
return hasValidFilletSelection({
|
context: { selectionRanges },
|
||||||
|
}) => {
|
||||||
|
return hasValidEdgeTreatmentSelection({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
code: codeManager.code,
|
code: codeManager.code,
|
||||||
@ -594,15 +632,11 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
'Has exportable geometry': () => {
|
'Has exportable geometry': () => {
|
||||||
if (
|
if (!kclManager.hasErrors() && kclManager.ast.body.length > 0)
|
||||||
kclManager.kclErrors.length === 0 &&
|
|
||||||
kclManager.ast.body.length > 0
|
|
||||||
)
|
|
||||||
return true
|
return true
|
||||||
else {
|
else {
|
||||||
let errorMessage = 'Unable to Export '
|
let errorMessage = 'Unable to Export '
|
||||||
if (kclManager.kclErrors.length > 0)
|
if (kclManager.hasErrors()) errorMessage += 'due to KCL Errors'
|
||||||
errorMessage += 'due to KCL Errors'
|
|
||||||
else if (kclManager.ast.body.length === 0)
|
else if (kclManager.ast.body.length === 0)
|
||||||
errorMessage += 'due to Empty Scene'
|
errorMessage += 'due to Empty Scene'
|
||||||
console.error(errorMessage)
|
console.error(errorMessage)
|
||||||
@ -720,7 +754,11 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'setHorzDistance',
|
constraint: 'setHorzDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -761,7 +799,10 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'setVertDistance',
|
constraint: 'setVertDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -809,7 +850,10 @@ export const ModelingMachineProvider = ({
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
angleOrLength: 'setAngle',
|
angleOrLength: 'setAngle',
|
||||||
}))
|
}))
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (err(_modifiedAst)) return Promise.reject(_modifiedAst)
|
if (err(_modifiedAst)) return Promise.reject(_modifiedAst)
|
||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
@ -851,7 +895,10 @@ export const ModelingMachineProvider = ({
|
|||||||
await applyConstraintAngleLength({
|
await applyConstraintAngleLength({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -891,7 +938,10 @@ export const ModelingMachineProvider = ({
|
|||||||
await applyConstraintIntersect({
|
await applyConstraintIntersect({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -932,7 +982,10 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'xAbs',
|
constraint: 'xAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -973,7 +1026,10 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'yAbs',
|
constraint: 'yAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -1014,9 +1070,10 @@ export const ModelingMachineProvider = ({
|
|||||||
const { variableName } = await getVarNameModal({
|
const { variableName } = await getVarNameModal({
|
||||||
valueName: data?.variableName || 'var',
|
valueName: data?.variableName || 'var',
|
||||||
})
|
})
|
||||||
let parsed = parse(recast(kclManager.ast))
|
let pResult = parse(recast(kclManager.ast))
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
parsed = parsed as Node<Program>
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
let parsed = pResult.program
|
||||||
|
|
||||||
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
||||||
moveValueIntoNewVariablePath(
|
moveValueIntoNewVariablePath(
|
||||||
@ -1025,7 +1082,11 @@ export const ModelingMachineProvider = ({
|
|||||||
data?.pathToNode || [],
|
data?.pathToNode || [],
|
||||||
variableName
|
variableName
|
||||||
)
|
)
|
||||||
parsed = parse(recast(_modifiedAst))
|
pResult = parse(recast(_modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
parsed = pResult.program
|
||||||
|
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(parsed)) return Promise.reject(parsed)
|
||||||
parsed = parsed as Node<Program>
|
parsed = parsed as Node<Program>
|
||||||
if (!pathToReplacedNode)
|
if (!pathToReplacedNode)
|
||||||
|
@ -40,7 +40,9 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
|
|||||||
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
|
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<button
|
<button
|
||||||
onClick={() => kclManager.format()}
|
onClick={() => {
|
||||||
|
kclManager.format().catch(reportRejection)
|
||||||
|
}}
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
>
|
>
|
||||||
<span>Format code</span>
|
<span>Format code</span>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { processMemory } from './MemoryPane'
|
import { processMemory } from './MemoryPane'
|
||||||
import { enginelessExecutor } from '../../../lib/testHelpers'
|
import { enginelessExecutor } from '../../../lib/testHelpers'
|
||||||
import { initPromise, parse, ProgramMemory } from '../../../lang/wasm'
|
import { assertParse, initPromise, ProgramMemory } from '../../../lang/wasm'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -28,12 +28,16 @@ describe('processMemory', () => {
|
|||||||
|> lineTo([0.98, 5.16], %)
|
|> lineTo([0.98, 5.16], %)
|
||||||
|> lineTo([2.15, 4.32], %)
|
|> lineTo([2.15, 4.32], %)
|
||||||
// |> rx(90, %)`
|
// |> rx(90, %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
||||||
const output = processMemory(execState.memory)
|
const output = processMemory(execState.memory)
|
||||||
expect(output.myVar).toEqual(5)
|
expect(output.myVar).toEqual(5)
|
||||||
expect(output.otherVar).toEqual(3)
|
expect(output.otherVar).toEqual(3)
|
||||||
expect(output).toEqual({
|
expect(output).toEqual({
|
||||||
|
HALF_TURN: 180,
|
||||||
|
QUARTER_TURN: 90,
|
||||||
|
THREE_QUARTER_TURN: 270,
|
||||||
|
ZERO: 0,
|
||||||
myVar: 5,
|
myVar: 5,
|
||||||
myFn: '__function(a)__',
|
myFn: '__function(a)__',
|
||||||
otherVar: 3,
|
otherVar: 3,
|
||||||
|
@ -90,7 +90,7 @@ export const sidebarPanes: SidebarPane[] = [
|
|||||||
keybinding: 'Shift + C',
|
keybinding: 'Shift + C',
|
||||||
showBadge: {
|
showBadge: {
|
||||||
value: ({ kclContext }) => {
|
value: ({ kclContext }) => {
|
||||||
return kclContext.errors.length
|
return kclContext.diagnostics.length
|
||||||
},
|
},
|
||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -53,7 +53,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
|||||||
settings: settings.context,
|
settings: settings.context,
|
||||||
platform: getPlatformString(),
|
platform: getPlatformString(),
|
||||||
}),
|
}),
|
||||||
[kclContext.errors, settings.context]
|
[kclContext.diagnostics, settings.context]
|
||||||
)
|
)
|
||||||
|
|
||||||
const sidebarActions: SidebarAction[] = [
|
const sidebarActions: SidebarAction[] = [
|
||||||
|
@ -10,7 +10,7 @@ import { APP_NAME } from 'lib/constants'
|
|||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
import { useLspContext } from './LspProvider'
|
import { useLspContext } from './LspProvider'
|
||||||
import { engineCommandManager } from 'lib/singletons'
|
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
@ -68,8 +68,7 @@ function AppLogoLink({
|
|||||||
data-testid="app-logo"
|
data-testid="app-logo"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onProjectClose(file || null, project?.path || null, false)
|
onProjectClose(file || null, project?.path || null, false)
|
||||||
// Clear the scene and end the session.
|
kclManager.switchedFiles = true
|
||||||
engineCommandManager.endSession()
|
|
||||||
}}
|
}}
|
||||||
to={PATHS.HOME}
|
to={PATHS.HOME}
|
||||||
className={wrapperClassName + ' hover:before:brightness-110'}
|
className={wrapperClassName + ' hover:before:brightness-110'}
|
||||||
@ -190,8 +189,7 @@ function ProjectMenuPopover({
|
|||||||
className: !isDesktop() ? 'hidden' : '',
|
className: !isDesktop() ? 'hidden' : '',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
onProjectClose(file || null, project?.path || null, true)
|
onProjectClose(file || null, project?.path || null, true)
|
||||||
// Clear the scene and end the session.
|
kclManager.switchedFiles = true
|
||||||
engineCommandManager.endSession()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
].filter(
|
].filter(
|
||||||
|
@ -40,7 +40,10 @@ export function removeConstrainingValuesInfo({
|
|||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
graphSelections: nodes.map(
|
graphSelections: nodes.map(
|
||||||
(node): Selection => ({
|
(node): Selection => ({
|
||||||
codeRef: codeRefFromRange([node.start, node.end], kclManager.ast),
|
codeRef: codeRefFromRange(
|
||||||
|
[node.start, node.end, true],
|
||||||
|
kclManager.ast
|
||||||
|
),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,9 @@ export default class EditorManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setHighlightRange(range: Array<Selection['codeRef']['range']>): void {
|
setHighlightRange(range: Array<Selection['codeRef']['range']>): void {
|
||||||
this._highlightRange = range
|
this._highlightRange = range.map((s): [number, number] => {
|
||||||
|
return [s[0], s[1]]
|
||||||
|
})
|
||||||
|
|
||||||
const selectionsWithSafeEnds = range.map((s): [number, number] => {
|
const selectionsWithSafeEnds = range.map((s): [number, number] => {
|
||||||
const safeEnd = Math.min(s[1], this._editorView?.state.doc.length || s[1])
|
const safeEnd = Math.min(s[1], this._editorView?.state.doc.length || s[1])
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
import { err, reportRejection } from 'lib/trap'
|
import { err, reportRejection } from 'lib/trap'
|
||||||
import { DefaultPlaneStr, getFaceDetails } from 'clientSideScene/sceneEntities'
|
import { DefaultPlaneStr, getFaceDetails } from 'clientSideScene/sceneEntities'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { CallExpression } from 'lang/wasm'
|
import { CallExpression, defaultSourceRange } from 'lang/wasm'
|
||||||
import { EdgeCutInfo, ExtrudeFacePlane } from 'machines/modelingMachine'
|
import { EdgeCutInfo, ExtrudeFacePlane } from 'machines/modelingMachine'
|
||||||
|
|
||||||
export function useEngineConnectionSubscriptions() {
|
export function useEngineConnectionSubscriptions() {
|
||||||
@ -46,7 +46,7 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
(editorManager.highlightRange[0][0] !== 0 &&
|
(editorManager.highlightRange[0][0] !== 0 &&
|
||||||
editorManager.highlightRange[0][1] !== 0)
|
editorManager.highlightRange[0][1] !== 0)
|
||||||
) {
|
) {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -201,7 +201,7 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
const { z_axis, y_axis, origin } = faceInfo
|
const { z_axis, y_axis, origin } = faceInfo
|
||||||
const sketchPathToNode = getNodePathFromSourceRange(
|
const sketchPathToNode = getNodePathFromSourceRange(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
err(codeRef) ? [0, 0] : codeRef.range
|
err(codeRef) ? defaultSourceRange() : codeRef.range
|
||||||
)
|
)
|
||||||
|
|
||||||
const getEdgeCutMeta = (): null | EdgeCutInfo => {
|
const getEdgeCutMeta = (): null | EdgeCutInfo => {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { KCLError } from './errors'
|
|
||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
import { type IndexLoaderData } from 'lib/types'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
import { useLoaderData } from 'react-router-dom'
|
import { useLoaderData } from 'react-router-dom'
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
|
|
||||||
const KclContext = createContext({
|
const KclContext = createContext({
|
||||||
code: codeManager?.code || '',
|
code: codeManager?.code || '',
|
||||||
programMemory: kclManager?.programMemory,
|
programMemory: kclManager?.programMemory,
|
||||||
ast: kclManager?.ast,
|
ast: kclManager?.ast,
|
||||||
isExecuting: kclManager?.isExecuting,
|
isExecuting: kclManager?.isExecuting,
|
||||||
errors: kclManager?.kclErrors,
|
diagnostics: kclManager?.diagnostics,
|
||||||
logs: kclManager?.logs,
|
logs: kclManager?.logs,
|
||||||
wasmInitFailed: kclManager?.wasmInitFailed,
|
wasmInitFailed: kclManager?.wasmInitFailed,
|
||||||
})
|
})
|
||||||
@ -32,7 +32,7 @@ export function KclContextProvider({
|
|||||||
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
||||||
const [ast, setAst] = useState(kclManager.ast)
|
const [ast, setAst] = useState(kclManager.ast)
|
||||||
const [isExecuting, setIsExecuting] = useState(false)
|
const [isExecuting, setIsExecuting] = useState(false)
|
||||||
const [errors, setErrors] = useState<KCLError[]>([])
|
const [diagnostics, setErrors] = useState<Diagnostic[]>([])
|
||||||
const [logs, setLogs] = useState<string[]>([])
|
const [logs, setLogs] = useState<string[]>([])
|
||||||
const [wasmInitFailed, setWasmInitFailed] = useState(false)
|
const [wasmInitFailed, setWasmInitFailed] = useState(false)
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ export function KclContextProvider({
|
|||||||
programMemory,
|
programMemory,
|
||||||
ast,
|
ast,
|
||||||
isExecuting,
|
isExecuting,
|
||||||
errors,
|
diagnostics,
|
||||||
logs,
|
logs,
|
||||||
wasmInitFailed,
|
wasmInitFailed,
|
||||||
}}
|
}}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { executeAst, lintAst } from 'lang/langHelpers'
|
import { executeAst, lintAst } from 'lang/langHelpers'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { KCLError, kclErrorsToDiagnostics } from './errors'
|
import {
|
||||||
|
KCLError,
|
||||||
|
complilationErrorsToDiagnostics,
|
||||||
|
kclErrorsToDiagnostics,
|
||||||
|
} from './errors'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { EngineCommandManager } from './std/engineConnection'
|
import { EngineCommandManager } from './std/engineConnection'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
@ -8,6 +12,7 @@ import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
CallExpression,
|
CallExpression,
|
||||||
|
clearSceneAndBustCache,
|
||||||
emptyExecState,
|
emptyExecState,
|
||||||
ExecState,
|
ExecState,
|
||||||
initPromise,
|
initPromise,
|
||||||
@ -51,11 +56,12 @@ export class KclManager {
|
|||||||
private _programMemory: ProgramMemory = ProgramMemory.empty()
|
private _programMemory: ProgramMemory = ProgramMemory.empty()
|
||||||
lastSuccessfulProgramMemory: ProgramMemory = ProgramMemory.empty()
|
lastSuccessfulProgramMemory: ProgramMemory = ProgramMemory.empty()
|
||||||
private _logs: string[] = []
|
private _logs: string[] = []
|
||||||
private _lints: Diagnostic[] = []
|
private _diagnostics: Diagnostic[] = []
|
||||||
private _kclErrors: KCLError[] = []
|
|
||||||
private _isExecuting = false
|
private _isExecuting = false
|
||||||
private _executeIsStale: ExecuteArgs | null = null
|
private _executeIsStale: ExecuteArgs | null = null
|
||||||
private _wasmInitFailed = true
|
private _wasmInitFailed = true
|
||||||
|
private _hasErrors = false
|
||||||
|
private _switchedFiles = false
|
||||||
|
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
|
|
||||||
@ -63,7 +69,7 @@ export class KclManager {
|
|||||||
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
||||||
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
||||||
private _logsCallBack: (arg: string[]) => void = () => {}
|
private _logsCallBack: (arg: string[]) => void = () => {}
|
||||||
private _kclErrorsCallBack: (arg: KCLError[]) => void = () => {}
|
private _kclErrorsCallBack: (errors: Diagnostic[]) => void = () => {}
|
||||||
private _wasmInitFailedCallback: (arg: boolean) => void = () => {}
|
private _wasmInitFailedCallback: (arg: boolean) => void = () => {}
|
||||||
private _executeCallback: () => void = () => {}
|
private _executeCallback: () => void = () => {}
|
||||||
|
|
||||||
@ -75,6 +81,10 @@ export class KclManager {
|
|||||||
this._astCallBack(ast)
|
this._astCallBack(ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set switchedFiles(switchedFiles: boolean) {
|
||||||
|
this._switchedFiles = switchedFiles
|
||||||
|
}
|
||||||
|
|
||||||
get programMemory() {
|
get programMemory() {
|
||||||
return this._programMemory
|
return this._programMemory
|
||||||
}
|
}
|
||||||
@ -84,7 +94,7 @@ export class KclManager {
|
|||||||
this._programMemoryCallBack(programMemory)
|
this._programMemoryCallBack(programMemory)
|
||||||
}
|
}
|
||||||
|
|
||||||
set execState(execState) {
|
private set execState(execState) {
|
||||||
this._execState = execState
|
this._execState = execState
|
||||||
this.programMemory = execState.memory
|
this.programMemory = execState.memory
|
||||||
}
|
}
|
||||||
@ -101,38 +111,28 @@ export class KclManager {
|
|||||||
this._logsCallBack(logs)
|
this._logsCallBack(logs)
|
||||||
}
|
}
|
||||||
|
|
||||||
get lints() {
|
get diagnostics() {
|
||||||
return this._lints
|
return this._diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
set lints(lints) {
|
set diagnostics(ds) {
|
||||||
if (lints === this._lints) return
|
if (ds === this._diagnostics) return
|
||||||
this._lints = lints
|
this._diagnostics = ds
|
||||||
// Run the lints through the diagnostics.
|
|
||||||
this.kclErrors = this._kclErrors
|
|
||||||
}
|
|
||||||
|
|
||||||
get kclErrors() {
|
|
||||||
return this._kclErrors
|
|
||||||
}
|
|
||||||
set kclErrors(kclErrors) {
|
|
||||||
if (kclErrors === this._kclErrors && this.lints.length === 0) return
|
|
||||||
this._kclErrors = kclErrors
|
|
||||||
this.setDiagnosticsForCurrentErrors()
|
this.setDiagnosticsForCurrentErrors()
|
||||||
this._kclErrorsCallBack(kclErrors)
|
}
|
||||||
|
|
||||||
|
addDiagnostics(ds: Diagnostic[]) {
|
||||||
|
if (ds.length === 0) return
|
||||||
|
this.diagnostics = this.diagnostics.concat(ds)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasErrors(): boolean {
|
||||||
|
return this._hasErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
setDiagnosticsForCurrentErrors() {
|
setDiagnosticsForCurrentErrors() {
|
||||||
let diagnostics = kclErrorsToDiagnostics(this.kclErrors)
|
editorManager?.setDiagnostics(this.diagnostics)
|
||||||
if (this.lints.length > 0) {
|
this._kclErrorsCallBack(this.diagnostics)
|
||||||
diagnostics = diagnostics.concat(this.lints)
|
|
||||||
}
|
|
||||||
editorManager?.setDiagnostics(diagnostics)
|
|
||||||
}
|
|
||||||
|
|
||||||
addKclErrors(kclErrors: KCLError[]) {
|
|
||||||
if (kclErrors.length === 0) return
|
|
||||||
this.kclErrors = this.kclErrors.concat(kclErrors)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isExecuting() {
|
get isExecuting() {
|
||||||
@ -172,8 +172,12 @@ export class KclManager {
|
|||||||
this.engineCommandManager = engineCommandManager
|
this.engineCommandManager = engineCommandManager
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.ensureWasmInit().then(() => {
|
this.ensureWasmInit().then(async () => {
|
||||||
this.ast = this.safeParse(codeManager.code) || this.ast
|
await this.safeParse(codeManager.code).then((ast) => {
|
||||||
|
if (ast) {
|
||||||
|
this.ast = ast
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +192,7 @@ export class KclManager {
|
|||||||
setProgramMemory: (arg: ProgramMemory) => void
|
setProgramMemory: (arg: ProgramMemory) => void
|
||||||
setAst: (arg: Node<Program>) => void
|
setAst: (arg: Node<Program>) => void
|
||||||
setLogs: (arg: string[]) => void
|
setLogs: (arg: string[]) => void
|
||||||
setKclErrors: (arg: KCLError[]) => void
|
setKclErrors: (errors: Diagnostic[]) => void
|
||||||
setIsExecuting: (arg: boolean) => void
|
setIsExecuting: (arg: boolean) => void
|
||||||
setWasmInitFailed: (arg: boolean) => void
|
setWasmInitFailed: (arg: boolean) => void
|
||||||
}) {
|
}) {
|
||||||
@ -217,20 +221,50 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
safeParse(code: string): Node<Program> | null {
|
// (jess) I'm not in love with this, but it ensures we clear the scene and
|
||||||
const ast = parse(code)
|
// bust the cache on
|
||||||
this.lints = []
|
// errors from parsing when opening new files.
|
||||||
this.kclErrors = []
|
// Why not just clear the cache on all parse errors, you ask? well its actually
|
||||||
if (!err(ast)) return ast
|
// really nice to keep the cache on parse errors within the same file, and
|
||||||
const kclerror: KCLError = ast as KCLError
|
// only bust on engine errors esp if they take a long time to execute and
|
||||||
|
// you hit the wrong key!
|
||||||
|
private async checkIfSwitchedFilesShouldClear() {
|
||||||
|
// If we were switching files and we hit an error on parse we need to bust
|
||||||
|
// the cache and clear the scene.
|
||||||
|
if (this._hasErrors && this._switchedFiles) {
|
||||||
|
await clearSceneAndBustCache(this.engineCommandManager)
|
||||||
|
} else if (this._switchedFiles) {
|
||||||
|
// Reset the switched files boolean.
|
||||||
|
this._switchedFiles = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.addKclErrors([kclerror])
|
async safeParse(code: string): Promise<Node<Program> | null> {
|
||||||
// TODO: re-eval if session should end?
|
const result = parse(code)
|
||||||
if (kclerror.msg === 'file is empty')
|
this.diagnostics = []
|
||||||
this.engineCommandManager?.endSession()
|
this._hasErrors = false
|
||||||
|
|
||||||
|
if (err(result)) {
|
||||||
|
const kclerror: KCLError = result as KCLError
|
||||||
|
this.diagnostics = kclErrorsToDiagnostics([kclerror])
|
||||||
|
this._hasErrors = true
|
||||||
|
|
||||||
|
await this.checkIfSwitchedFilesShouldClear()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.addDiagnostics(complilationErrorsToDiagnostics(result.errors))
|
||||||
|
this.addDiagnostics(complilationErrorsToDiagnostics(result.warnings))
|
||||||
|
if (result.errors.length > 0) {
|
||||||
|
this._hasErrors = true
|
||||||
|
|
||||||
|
await this.checkIfSwitchedFilesShouldClear()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.program
|
||||||
|
}
|
||||||
|
|
||||||
async ensureWasmInit() {
|
async ensureWasmInit() {
|
||||||
try {
|
try {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -267,19 +301,16 @@ export class KclManager {
|
|||||||
this._cancelTokens.set(currentExecutionId, false)
|
this._cancelTokens.set(currentExecutionId, false)
|
||||||
|
|
||||||
this.isExecuting = true
|
this.isExecuting = true
|
||||||
// Make sure we clear before starting again. End session will do this.
|
|
||||||
this.engineCommandManager?.endSession()
|
|
||||||
await this.ensureWasmInit()
|
await this.ensureWasmInit()
|
||||||
const { logs, errors, execState, isInterrupted } = await executeAst({
|
const { logs, errors, execState, isInterrupted } = await executeAst({
|
||||||
ast,
|
ast,
|
||||||
idGenerator: this.execState.idGenerator,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Program was not interrupted, setup the scene
|
// Program was not interrupted, setup the scene
|
||||||
// Do not send send scene commands if the program was interrupted, go to clean up
|
// Do not send send scene commands if the program was interrupted, go to clean up
|
||||||
if (!isInterrupted) {
|
if (!isInterrupted) {
|
||||||
this.lints = await lintAst({ ast: ast })
|
this.addDiagnostics(await lintAst({ ast: ast }))
|
||||||
|
|
||||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||||
setSelectionFilterToDefault(execState.memory, this.engineCommandManager)
|
setSelectionFilterToDefault(execState.memory, this.engineCommandManager)
|
||||||
@ -321,9 +352,7 @@ export class KclManager {
|
|||||||
|
|
||||||
this.logs = logs
|
this.logs = logs
|
||||||
// Do not add the errors since the program was interrupted and the error is not a real KCL error
|
// Do not add the errors since the program was interrupted and the error is not a real KCL error
|
||||||
this.addKclErrors(isInterrupted ? [] : errors)
|
this.addDiagnostics(isInterrupted ? [] : kclErrorsToDiagnostics(errors))
|
||||||
// Reset the next ID index so that we reuse the previous IDs next time.
|
|
||||||
execState.idGenerator.nextId = 0
|
|
||||||
this.execState = execState
|
this.execState = execState
|
||||||
if (!errors.length) {
|
if (!errors.length) {
|
||||||
this.lastSuccessfulProgramMemory = execState.memory
|
this.lastSuccessfulProgramMemory = execState.memory
|
||||||
@ -355,7 +384,7 @@ export class KclManager {
|
|||||||
console.error(newCode)
|
console.error(newCode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newAst = this.safeParse(newCode)
|
const newAst = await this.safeParse(newCode)
|
||||||
if (!newAst) {
|
if (!newAst) {
|
||||||
this.clearAst()
|
this.clearAst()
|
||||||
return
|
return
|
||||||
@ -364,13 +393,13 @@ export class KclManager {
|
|||||||
|
|
||||||
const { logs, errors, execState } = await executeAst({
|
const { logs, errors, execState } = await executeAst({
|
||||||
ast: newAst,
|
ast: newAst,
|
||||||
idGenerator: this.execState.idGenerator,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
useFakeExecutor: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
|
|
||||||
this._logs = logs
|
this._logs = logs
|
||||||
this._kclErrors = errors
|
this.addDiagnostics(kclErrorsToDiagnostics(errors))
|
||||||
this._execState = execState
|
this._execState = execState
|
||||||
this._programMemory = execState.memory
|
this._programMemory = execState.memory
|
||||||
if (!errors.length) {
|
if (!errors.length) {
|
||||||
@ -398,7 +427,7 @@ export class KclManager {
|
|||||||
...artifact,
|
...artifact,
|
||||||
codeRef: {
|
codeRef: {
|
||||||
...artifact.codeRef,
|
...artifact.codeRef,
|
||||||
range: [node.start, node.end],
|
range: [node.start, node.end, true],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -410,7 +439,7 @@ export class KclManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
async executeCode(zoomToFit?: boolean): Promise<void> {
|
async executeCode(zoomToFit?: boolean): Promise<void> {
|
||||||
const ast = this.safeParse(codeManager.code)
|
const ast = await this.safeParse(codeManager.code)
|
||||||
if (!ast) {
|
if (!ast) {
|
||||||
this.clearAst()
|
this.clearAst()
|
||||||
return
|
return
|
||||||
@ -418,9 +447,9 @@ export class KclManager {
|
|||||||
this.ast = { ...ast }
|
this.ast = { ...ast }
|
||||||
return this.executeAst({ zoomToFit })
|
return this.executeAst({ zoomToFit })
|
||||||
}
|
}
|
||||||
format() {
|
async format() {
|
||||||
const originalCode = codeManager.code
|
const originalCode = codeManager.code
|
||||||
const ast = this.safeParse(originalCode)
|
const ast = await this.safeParse(originalCode)
|
||||||
if (!ast) {
|
if (!ast) {
|
||||||
this.clearAst()
|
this.clearAst()
|
||||||
return
|
return
|
||||||
@ -460,7 +489,7 @@ export class KclManager {
|
|||||||
const newCode = recast(ast)
|
const newCode = recast(ast)
|
||||||
if (err(newCode)) return Promise.reject(newCode)
|
if (err(newCode)) return Promise.reject(newCode)
|
||||||
|
|
||||||
const astWithUpdatedSource = this.safeParse(newCode)
|
const astWithUpdatedSource = await this.safeParse(newCode)
|
||||||
if (!astWithUpdatedSource) return Promise.reject(new Error('bad ast'))
|
if (!astWithUpdatedSource) return Promise.reject(new Error('bad ast'))
|
||||||
let returnVal: Selections | undefined = undefined
|
let returnVal: Selections | undefined = undefined
|
||||||
|
|
||||||
@ -490,7 +519,7 @@ export class KclManager {
|
|||||||
if (start && end) {
|
if (start && end) {
|
||||||
returnVal.graphSelections.push({
|
returnVal.graphSelections.push({
|
||||||
codeRef: {
|
codeRef: {
|
||||||
range: [start, end],
|
range: [start, end, true],
|
||||||
pathToNode: path,
|
pathToNode: path,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse, initPromise } from './wasm'
|
import { assertParse, initPromise } from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -14,7 +14,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)`
|
// |> rx(45, %)`
|
||||||
const execState = await enginelessExecutor(parse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const sketch001 = execState.memory.get('mySketch001')
|
const sketch001 = execState.memory.get('mySketch001')
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
@ -67,7 +67,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> extrude(2, %)`
|
|> extrude(2, %)`
|
||||||
const execState = await enginelessExecutor(parse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const sketch001 = execState.memory.get('mySketch001')
|
const sketch001 = execState.memory.get('mySketch001')
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
@ -147,7 +147,7 @@ const sk2 = startSketchOn('XY')
|
|||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
|
|
||||||
`
|
`
|
||||||
const execState = await enginelessExecutor(parse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const geos = [programMemory.get('theExtrude'), programMemory.get('sk2')]
|
const geos = [programMemory.get('theExtrude'), programMemory.get('sk2')]
|
||||||
|
@ -8,20 +8,14 @@ describe('test kclErrToDiagnostic', () => {
|
|||||||
message: '',
|
message: '',
|
||||||
kind: 'semantic',
|
kind: 'semantic',
|
||||||
msg: 'Semantic error',
|
msg: 'Semantic error',
|
||||||
sourceRanges: [
|
sourceRange: [0, 1, true],
|
||||||
[0, 1, 0],
|
|
||||||
[2, 3, 0],
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
message: '',
|
message: '',
|
||||||
kind: 'type',
|
kind: 'type',
|
||||||
msg: 'Type error',
|
msg: 'Type error',
|
||||||
sourceRanges: [
|
sourceRange: [4, 5, true],
|
||||||
[4, 5, 0],
|
|
||||||
[6, 7, 0],
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
const diagnostics = kclErrorsToDiagnostics(errors)
|
const diagnostics = kclErrorsToDiagnostics(errors)
|
||||||
@ -32,24 +26,12 @@ describe('test kclErrToDiagnostic', () => {
|
|||||||
message: 'Semantic error',
|
message: 'Semantic error',
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
from: 2,
|
|
||||||
to: 3,
|
|
||||||
message: 'Semantic error',
|
|
||||||
severity: 'error',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
from: 4,
|
from: 4,
|
||||||
to: 5,
|
to: 5,
|
||||||
message: 'Type error',
|
message: 'Type error',
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
from: 6,
|
|
||||||
to: 7,
|
|
||||||
message: 'Type error',
|
|
||||||
severity: 'error',
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,88 +1,90 @@
|
|||||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||||
|
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
||||||
import { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint'
|
import { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint'
|
||||||
import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
||||||
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
||||||
import { Text } from '@codemirror/state'
|
import { Text } from '@codemirror/state'
|
||||||
|
import { EditorView } from 'codemirror'
|
||||||
const TOP_LEVEL_MODULE_ID = 0
|
import { SourceRange } from 'lang/wasm'
|
||||||
|
|
||||||
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
||||||
export class KCLError extends Error {
|
export class KCLError extends Error {
|
||||||
kind: ExtractKind<RustKclError> | 'name'
|
kind: ExtractKind<RustKclError> | 'name'
|
||||||
sourceRanges: [number, number, number][]
|
sourceRange: SourceRange
|
||||||
msg: string
|
msg: string
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
kind: ExtractKind<RustKclError> | 'name',
|
kind: ExtractKind<RustKclError> | 'name',
|
||||||
msg: string,
|
msg: string,
|
||||||
sourceRanges: [number, number, number][]
|
sourceRange: SourceRange
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.kind = kind
|
this.kind = kind
|
||||||
this.msg = msg
|
this.msg = msg
|
||||||
this.sourceRanges = sourceRanges
|
this.sourceRange = sourceRange
|
||||||
Object.setPrototypeOf(this, KCLError.prototype)
|
Object.setPrototypeOf(this, KCLError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLLexicalError extends KCLError {
|
export class KCLLexicalError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('lexical', msg, sourceRanges)
|
super('lexical', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLInternalError extends KCLError {
|
export class KCLInternalError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('internal', msg, sourceRanges)
|
super('internal', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLSyntaxError extends KCLError {
|
export class KCLSyntaxError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('syntax', msg, sourceRanges)
|
super('syntax', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLSemanticError extends KCLError {
|
export class KCLSemanticError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('semantic', msg, sourceRanges)
|
super('semantic', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLTypeError extends KCLError {
|
export class KCLTypeError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('type', msg, sourceRanges)
|
super('type', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUnimplementedError extends KCLError {
|
export class KCLUnimplementedError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('unimplemented', msg, sourceRanges)
|
super('unimplemented', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUnexpectedError extends KCLError {
|
export class KCLUnexpectedError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('unexpected', msg, sourceRanges)
|
super('unexpected', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLValueAlreadyDefined extends KCLError {
|
export class KCLValueAlreadyDefined extends KCLError {
|
||||||
constructor(key: string, sourceRanges: [number, number, number][]) {
|
constructor(key: string, sourceRange: SourceRange) {
|
||||||
super('name', `Key ${key} was already defined elsewhere`, sourceRanges)
|
super('name', `Key ${key} was already defined elsewhere`, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUndefinedValueError extends KCLError {
|
export class KCLUndefinedValueError extends KCLError {
|
||||||
constructor(key: string, sourceRanges: [number, number, number][]) {
|
constructor(key: string, sourceRange: SourceRange) {
|
||||||
super('name', `Key ${key} has not been defined`, sourceRanges)
|
super('name', `Key ${key} has not been defined`, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,27 +101,14 @@ export function lspDiagnosticsToKclErrors(
|
|||||||
.flatMap(
|
.flatMap(
|
||||||
({ range, message }) =>
|
({ range, message }) =>
|
||||||
new KCLError('unexpected', message, [
|
new KCLError('unexpected', message, [
|
||||||
[
|
|
||||||
posToOffset(doc, range.start)!,
|
posToOffset(doc, range.start)!,
|
||||||
posToOffset(doc, range.end)!,
|
posToOffset(doc, range.end)!,
|
||||||
TOP_LEVEL_MODULE_ID,
|
true,
|
||||||
],
|
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
.filter(({ sourceRanges }) => {
|
|
||||||
const [from, to, moduleId] = sourceRanges[0]
|
|
||||||
return (
|
|
||||||
from !== null &&
|
|
||||||
to !== null &&
|
|
||||||
from !== undefined &&
|
|
||||||
to !== undefined &&
|
|
||||||
// Filter out errors that are not from the top-level module.
|
|
||||||
moduleId === TOP_LEVEL_MODULE_ID
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const c = a.sourceRanges[0][0]
|
const c = a.sourceRange[0]
|
||||||
const d = b.sourceRanges[0][0]
|
const d = b.sourceRange[0]
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case c < d:
|
case c < d:
|
||||||
return -1
|
return -1
|
||||||
@ -137,17 +126,48 @@ export function lspDiagnosticsToKclErrors(
|
|||||||
export function kclErrorsToDiagnostics(
|
export function kclErrorsToDiagnostics(
|
||||||
errors: KCLError[]
|
errors: KCLError[]
|
||||||
): CodeMirrorDiagnostic[] {
|
): CodeMirrorDiagnostic[] {
|
||||||
return errors?.flatMap((err) => {
|
return errors
|
||||||
const sourceRanges: CodeMirrorDiagnostic[] = err.sourceRanges
|
?.filter((err) => err.sourceRange[2])
|
||||||
// Filter out errors that are not from the top-level module.
|
.map((err) => {
|
||||||
.filter(([_start, _end, moduleId]) => moduleId === TOP_LEVEL_MODULE_ID)
|
return {
|
||||||
.map(([from, to]) => {
|
from: err.sourceRange[0],
|
||||||
return { from, to, message: err.msg, severity: 'error' }
|
to: err.sourceRange[1],
|
||||||
})
|
message: err.msg,
|
||||||
// Make sure we didn't filter out all the source ranges.
|
severity: 'error',
|
||||||
if (sourceRanges.length === 0) {
|
}
|
||||||
sourceRanges.push({ from: 0, to: 0, message: err.msg, severity: 'error' })
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function complilationErrorsToDiagnostics(
|
||||||
|
errors: CompilationError[]
|
||||||
|
): CodeMirrorDiagnostic[] {
|
||||||
|
return errors
|
||||||
|
?.filter((err) => err.sourceRange[2] === 0)
|
||||||
|
.map((err) => {
|
||||||
|
let severity: any = 'error'
|
||||||
|
if (err.severity === 'Warning') {
|
||||||
|
severity = 'warning'
|
||||||
|
}
|
||||||
|
let actions
|
||||||
|
const suggestion = err.suggestion
|
||||||
|
if (suggestion) {
|
||||||
|
actions = [
|
||||||
|
{
|
||||||
|
name: suggestion.title,
|
||||||
|
apply: (view: EditorView, from: number, to: number) => {
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from, to, insert: suggestion.insert },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
from: err.sourceRange[0],
|
||||||
|
to: err.sourceRange[1],
|
||||||
|
message: err.message,
|
||||||
|
severity,
|
||||||
|
actions,
|
||||||
}
|
}
|
||||||
return sourceRanges
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
parse,
|
assertParse,
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
Sketch,
|
Sketch,
|
||||||
initPromise,
|
initPromise,
|
||||||
@ -472,7 +472,7 @@ describe('Testing Errors', () => {
|
|||||||
const theExtrude = startSketchOn('XY')
|
const theExtrude = startSketchOn('XY')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([-2.4, 5], %)
|
|> line([-2.4, 5], %)
|
||||||
|> line([-0.76], myVarZ, %)
|
|> line(myVarZ, %)
|
||||||
|> line([5,5], %)
|
|> line([5,5], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(4, %)`
|
|> extrude(4, %)`
|
||||||
@ -480,7 +480,7 @@ const theExtrude = startSketchOn('XY')
|
|||||||
new KCLError(
|
new KCLError(
|
||||||
'undefined_value',
|
'undefined_value',
|
||||||
'memory item key `myVarZ` is not defined',
|
'memory item key `myVarZ` is not defined',
|
||||||
[[129, 135, 0]]
|
[129, 135, true]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -492,7 +492,7 @@ async function exe(
|
|||||||
code: string,
|
code: string,
|
||||||
programMemory: ProgramMemory = ProgramMemory.empty()
|
programMemory: ProgramMemory = ProgramMemory.empty()
|
||||||
) {
|
) {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast, programMemory)
|
const execState = await enginelessExecutor(ast, programMemory)
|
||||||
return execState.memory
|
return execState.memory
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
||||||
import { Identifier, parse, initPromise, Parameter } from './wasm'
|
import { Identifier, assertParse, initPromise, Parameter } from './wasm'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -17,19 +17,19 @@ const sk3 = startSketchAt([0, 0])
|
|||||||
`
|
`
|
||||||
const subStr = 'lineTo([3, 4], %, $yo)'
|
const subStr = 'lineTo([3, 4], %, $yo)'
|
||||||
const lineToSubstringIndex = code.indexOf(subStr)
|
const lineToSubstringIndex = code.indexOf(subStr)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number, boolean] = [
|
||||||
lineToSubstringIndex,
|
lineToSubstringIndex,
|
||||||
lineToSubstringIndex + subStr.length,
|
lineToSubstringIndex + subStr.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const _node = getNodeFromPath<any>(ast, nodePath)
|
const _node = getNodeFromPath<any>(ast, nodePath)
|
||||||
if (err(_node)) throw _node
|
if (err(_node)) throw _node
|
||||||
const { node } = _node
|
const { node } = _node
|
||||||
|
|
||||||
expect([node.start, node.end]).toEqual(sourceRange)
|
expect([node.start, node.end, true]).toEqual(sourceRange)
|
||||||
expect(node.type).toBe('CallExpression')
|
expect(node.type).toBe('CallExpression')
|
||||||
})
|
})
|
||||||
it('gets path right for function definition params', () => {
|
it('gets path right for function definition params', () => {
|
||||||
@ -45,13 +45,13 @@ const sk3 = startSketchAt([0, 0])
|
|||||||
const b1 = cube([0,0], 10)`
|
const b1 = cube([0,0], 10)`
|
||||||
const subStr = 'pos, scale'
|
const subStr = 'pos, scale'
|
||||||
const subStrIndex = code.indexOf(subStr)
|
const subStrIndex = code.indexOf(subStr)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number, boolean] = [
|
||||||
subStrIndex,
|
subStrIndex,
|
||||||
subStrIndex + 'pos'.length,
|
subStrIndex + 'pos'.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const _node = getNodeFromPath<Parameter>(ast, nodePath)
|
const _node = getNodeFromPath<Parameter>(ast, nodePath)
|
||||||
if (err(_node)) throw _node
|
if (err(_node)) throw _node
|
||||||
@ -60,8 +60,7 @@ const b1 = cube([0,0], 10)`
|
|||||||
expect(nodePath).toEqual([
|
expect(nodePath).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', ''],
|
['init', ''],
|
||||||
['params', 'FunctionExpression'],
|
['params', 'FunctionExpression'],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
@ -82,13 +81,13 @@ const b1 = cube([0,0], 10)`
|
|||||||
const b1 = cube([0,0], 10)`
|
const b1 = cube([0,0], 10)`
|
||||||
const subStr = 'scale, 0'
|
const subStr = 'scale, 0'
|
||||||
const subStrIndex = code.indexOf(subStr)
|
const subStrIndex = code.indexOf(subStr)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number, boolean] = [
|
||||||
subStrIndex,
|
subStrIndex,
|
||||||
subStrIndex + 'scale'.length,
|
subStrIndex + 'scale'.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const _node = getNodeFromPath<Identifier>(ast, nodePath)
|
const _node = getNodeFromPath<Identifier>(ast, nodePath)
|
||||||
if (err(_node)) throw _node
|
if (err(_node)) throw _node
|
||||||
@ -96,14 +95,12 @@ const b1 = cube([0,0], 10)`
|
|||||||
expect(nodePath).toEqual([
|
expect(nodePath).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', ''],
|
['init', ''],
|
||||||
['body', 'FunctionExpression'],
|
['body', 'FunctionExpression'],
|
||||||
['body', 'FunctionExpression'],
|
['body', 'FunctionExpression'],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', ''],
|
['init', ''],
|
||||||
['body', 'PipeExpression'],
|
['body', 'PipeExpression'],
|
||||||
[2, 'index'],
|
[2, 'index'],
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { parse, initPromise, programMemoryInit } from './wasm'
|
import { assertParse, initPromise, programMemoryInit } from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { assert } from 'vitest'
|
|
||||||
// These unit tests makes web requests to a public github repository.
|
// These unit tests makes web requests to a public github repository.
|
||||||
|
|
||||||
interface KclSampleFile {
|
interface KclSampleFile {
|
||||||
@ -58,8 +57,7 @@ describe('Test KCL Samples from public Github repository', () => {
|
|||||||
files.forEach((file: KclSampleFile) => {
|
files.forEach((file: KclSampleFile) => {
|
||||||
it(`should parse ${file.filename} without errors`, async () => {
|
it(`should parse ${file.filename} without errors`, async () => {
|
||||||
const code = await getKclSampleCodeFromGithub(file.filename)
|
const code = await getKclSampleCodeFromGithub(file.filename)
|
||||||
const parsed = parse(code)
|
assertParse(code)
|
||||||
assert(!(parsed instanceof Error))
|
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -71,9 +69,8 @@ describe('Test KCL Samples from public Github repository', () => {
|
|||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const file: KclSampleFile = files[i]
|
const file: KclSampleFile = files[i]
|
||||||
const code = await getKclSampleCodeFromGithub(file.filename)
|
const code = await getKclSampleCodeFromGithub(file.filename)
|
||||||
const parsed = parse(code)
|
const ast = assertParse(code)
|
||||||
assert(!(parsed instanceof Error))
|
await enginelessExecutor(ast, programMemoryInit())
|
||||||
await enginelessExecutor(parsed, programMemoryInit())
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
files.length * 1000
|
files.length * 1000
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
Program,
|
Program,
|
||||||
_executor,
|
_executor,
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
programMemoryInit,
|
|
||||||
kclLint,
|
kclLint,
|
||||||
emptyExecState,
|
emptyExecState,
|
||||||
ExecState,
|
ExecState,
|
||||||
@ -11,7 +10,6 @@ import { enginelessExecutor } from 'lib/testHelpers'
|
|||||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
import { KCLError } from 'lang/errors'
|
import { KCLError } from 'lang/errors'
|
||||||
import { Diagnostic } from '@codemirror/lint'
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export type ToolTip =
|
export type ToolTip =
|
||||||
@ -49,15 +47,13 @@ export const toolTips: Array<ToolTip> = [
|
|||||||
export async function executeAst({
|
export async function executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor = false,
|
// If you set programMemoryOverride we assume you mean mock mode. Since that
|
||||||
|
// is the only way to go about it.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator,
|
|
||||||
}: {
|
}: {
|
||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
useFakeExecutor?: boolean
|
|
||||||
programMemoryOverride?: ProgramMemory
|
programMemoryOverride?: ProgramMemory
|
||||||
idGenerator?: IdGenerator
|
|
||||||
isInterrupted?: boolean
|
isInterrupted?: boolean
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
logs: string[]
|
logs: string[]
|
||||||
@ -66,22 +62,14 @@ export async function executeAst({
|
|||||||
isInterrupted: boolean
|
isInterrupted: boolean
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
if (!useFakeExecutor) {
|
const execState = await (programMemoryOverride
|
||||||
engineCommandManager.endSession()
|
? enginelessExecutor(ast, programMemoryOverride)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
: _executor(ast, engineCommandManager))
|
||||||
engineCommandManager.startNewSession()
|
|
||||||
}
|
await engineCommandManager.waitForAllCommands(
|
||||||
const execState = await (useFakeExecutor
|
programMemoryOverride !== undefined
|
||||||
? enginelessExecutor(ast, programMemoryOverride || programMemoryInit())
|
)
|
||||||
: _executor(
|
|
||||||
ast,
|
|
||||||
programMemoryInit(),
|
|
||||||
idGenerator,
|
|
||||||
engineCommandManager,
|
|
||||||
false
|
|
||||||
))
|
|
||||||
|
|
||||||
await engineCommandManager.waitForAllCommands(useFakeExecutor)
|
|
||||||
return {
|
return {
|
||||||
logs: [],
|
logs: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse, recast, initPromise, Identifier } from './wasm'
|
import { assertParse, recast, initPromise, Identifier } from './wasm'
|
||||||
import {
|
import {
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
@ -82,11 +82,11 @@ describe('Testing createVariableDeclaration', () => {
|
|||||||
it('should create a variable declaration', () => {
|
it('should create a variable declaration', () => {
|
||||||
const result = createVariableDeclaration('myVar', createLiteral(5))
|
const result = createVariableDeclaration('myVar', createLiteral(5))
|
||||||
expect(result.type).toBe('VariableDeclaration')
|
expect(result.type).toBe('VariableDeclaration')
|
||||||
expect(result.declarations[0].type).toBe('VariableDeclarator')
|
expect(result.declaration.type).toBe('VariableDeclarator')
|
||||||
expect(result.declarations[0].id.type).toBe('Identifier')
|
expect(result.declaration.id.type).toBe('Identifier')
|
||||||
expect(result.declarations[0].id.name).toBe('myVar')
|
expect(result.declaration.id.name).toBe('myVar')
|
||||||
expect(result.declarations[0].init.type).toBe('Literal')
|
expect(result.declaration.init.type).toBe('Literal')
|
||||||
expect((result.declarations[0].init as any).value).toBe(5)
|
expect((result.declaration.init as any).value).toBe(5)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('Testing createPipeExpression', () => {
|
describe('Testing createPipeExpression', () => {
|
||||||
@ -146,10 +146,13 @@ function giveSketchFnCallTagTestHelper(
|
|||||||
// giveSketchFnCallTag inputs and outputs an ast, which is very verbose for testing
|
// giveSketchFnCallTag inputs and outputs an ast, which is very verbose for testing
|
||||||
// this wrapper changes the input and output to code
|
// this wrapper changes the input and output to code
|
||||||
// making it more of an integration test, but easier to read the test intention is the goal
|
// making it more of an integration test, but easier to read the test intention is the goal
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const start = code.indexOf(searchStr)
|
const start = code.indexOf(searchStr)
|
||||||
const range: [number, number] = [start, start + searchStr.length]
|
const range: [number, number, boolean] = [
|
||||||
|
start,
|
||||||
|
start + searchStr.length,
|
||||||
|
true,
|
||||||
|
]
|
||||||
const sketchRes = giveSketchFnCallTag(ast, range)
|
const sketchRes = giveSketchFnCallTag(ast, range)
|
||||||
if (err(sketchRes)) throw sketchRes
|
if (err(sketchRes)) throw sketchRes
|
||||||
const { modifiedAst, tag, isTagExisting } = sketchRes
|
const { modifiedAst, tag, isTagExisting } = sketchRes
|
||||||
@ -221,14 +224,13 @@ part001 = startSketchOn('XY')
|
|||||||
|> angledLine([jkl(yo) + 2, 3.09], %)
|
|> angledLine([jkl(yo) + 2, 3.09], %)
|
||||||
yo2 = hmm([identifierGuy + 5])`
|
yo2 = hmm([identifierGuy + 5])`
|
||||||
it('should move a binary expression into a new variable', async () => {
|
it('should move a binary expression into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('100 + 100') + 1
|
const startIndex = code.indexOf('100 + 100') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -236,14 +238,13 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('should move a value into a new variable', async () => {
|
it('should move a value into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('2.8') + 1
|
const startIndex = code.indexOf('2.8') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -251,14 +252,13 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(newCode).toContain(`line([newVar, 0], %)`)
|
expect(newCode).toContain(`line([newVar, 0], %)`)
|
||||||
})
|
})
|
||||||
it('should move a callExpression into a new variable', async () => {
|
it('should move a callExpression into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('def(')
|
const startIndex = code.indexOf('def(')
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -266,14 +266,13 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('should move a binary expression with call expression into a new variable', async () => {
|
it('should move a binary expression with call expression into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('jkl(') + 1
|
const startIndex = code.indexOf('jkl(') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -281,14 +280,13 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('should move a identifier into a new variable', async () => {
|
it('should move a identifier into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('identifierGuy +') + 1
|
const startIndex = code.indexOf('identifierGuy +') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -305,19 +303,20 @@ describe('testing sketchOnExtrudedFace', () => {
|
|||||||
|> line([8.62, -9.57], %)
|
|> line([8.62, -9.57], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5 + 7, %)`
|
|> extrude(5 + 7, %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const segmentSnippet = `line([9.7, 9.19], %)`
|
const segmentSnippet = `line([9.7, 9.19], %)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number, boolean] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
||||||
const extrudeSnippet = `extrude(5 + 7, %)`
|
const extrudeSnippet = `extrude(5 + 7, %)`
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(extrudeSnippet),
|
code.indexOf(extrudeSnippet),
|
||||||
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
|
|
||||||
@ -345,18 +344,19 @@ sketch001 = startSketchOn(part001, seg01)`)
|
|||||||
|> line([8.62, -9.57], %)
|
|> line([8.62, -9.57], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5 + 7, %)`
|
|> extrude(5 + 7, %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const segmentSnippet = `close(%)`
|
const segmentSnippet = `close(%)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number, boolean] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
||||||
const extrudeSnippet = `extrude(5 + 7, %)`
|
const extrudeSnippet = `extrude(5 + 7, %)`
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(extrudeSnippet),
|
code.indexOf(extrudeSnippet),
|
||||||
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
|
|
||||||
@ -384,18 +384,19 @@ sketch001 = startSketchOn(part001, seg01)`)
|
|||||||
|> line([8.62, -9.57], %)
|
|> line([8.62, -9.57], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5 + 7, %)`
|
|> extrude(5 + 7, %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const sketchSnippet = `startProfileAt([3.58, 2.06], %)`
|
const sketchSnippet = `startProfileAt([3.58, 2.06], %)`
|
||||||
const sketchRange: [number, number] = [
|
const sketchRange: [number, number, boolean] = [
|
||||||
code.indexOf(sketchSnippet),
|
code.indexOf(sketchSnippet),
|
||||||
code.indexOf(sketchSnippet) + sketchSnippet.length,
|
code.indexOf(sketchSnippet) + sketchSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
||||||
const extrudeSnippet = `extrude(5 + 7, %)`
|
const extrudeSnippet = `extrude(5 + 7, %)`
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(extrudeSnippet),
|
code.indexOf(extrudeSnippet),
|
||||||
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
|
|
||||||
@ -432,18 +433,19 @@ sketch001 = startSketchOn(part001, 'END')`)
|
|||||||
|> line([-17.67, 0.85], %)
|
|> line([-17.67, 0.85], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
part001 = extrude(5 + 7, sketch001)`
|
part001 = extrude(5 + 7, sketch001)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const segmentSnippet = `line([4.99, -0.46], %)`
|
const segmentSnippet = `line([4.99, -0.46], %)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number, boolean] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
||||||
const extrudeSnippet = `extrude(5 + 7, sketch001)`
|
const extrudeSnippet = `extrude(5 + 7, sketch001)`
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(extrudeSnippet),
|
code.indexOf(extrudeSnippet),
|
||||||
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
|
|
||||||
@ -466,13 +468,13 @@ describe('Testing deleteSegmentFromPipeExpression', () => {
|
|||||||
|> line([306.21, 198.82], %)
|
|> line([306.21, 198.82], %)
|
||||||
|> line([306.21, 198.85], %, $a)
|
|> line([306.21, 198.85], %, $a)
|
||||||
|> line([306.21, 198.87], %)`
|
|> line([306.21, 198.87], %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const lineOfInterest = 'line([306.21, 198.85], %, $a)'
|
const lineOfInterest = 'line([306.21, 198.85], %, $a)'
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
const modifiedAst = deleteSegmentFromPipeExpression(
|
const modifiedAst = deleteSegmentFromPipeExpression(
|
||||||
@ -544,13 +546,13 @@ ${!replace1 ? ` |> ${line}\n` : ''} |> angledLine([-65, ${
|
|||||||
],
|
],
|
||||||
])(`%s`, async (_, line, [replace1, replace2]) => {
|
])(`%s`, async (_, line, [replace1, replace2]) => {
|
||||||
const code = makeCode(line)
|
const code = makeCode(line)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const lineOfInterest = line
|
const lineOfInterest = line
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
const dependentSegments = findUsesOfTagInPipe(ast, pathToNode)
|
const dependentSegments = findUsesOfTagInPipe(ast, pathToNode)
|
||||||
@ -632,14 +634,14 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
],
|
],
|
||||||
['tangentialArcTo([3.14 + 0, 13.14], %)', 'arrayIndex', 1],
|
['tangentialArcTo([3.14 + 0, 13.14], %)', 'arrayIndex', 1],
|
||||||
] as const)('stdlib fn: %s', async (expectedFinish, key, value) => {
|
] as const)('stdlib fn: %s', async (expectedFinish, key, value) => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest) + 1,
|
code.indexOf(lineOfInterest) + 1,
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
let argPosition: SimplifiedArgDetails
|
let argPosition: SimplifiedArgDetails
|
||||||
@ -686,14 +688,14 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
['angledLineToX([12.14 + 0, 12], %)', 'arrayIndex', 1],
|
['angledLineToX([12.14 + 0, 12], %)', 'arrayIndex', 1],
|
||||||
['angledLineToY([30, 10.14 + 0], %)', 'arrayIndex', 0],
|
['angledLineToY([30, 10.14 + 0], %)', 'arrayIndex', 0],
|
||||||
])('stdlib fn: %s', async (expectedFinish, key, value) => {
|
])('stdlib fn: %s', async (expectedFinish, key, value) => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest) + 1,
|
code.indexOf(lineOfInterest) + 1,
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
let argPosition: SimplifiedArgDetails
|
let argPosition: SimplifiedArgDetails
|
||||||
if (key === 'arrayIndex' && typeof value === 'number') {
|
if (key === 'arrayIndex' && typeof value === 'number') {
|
||||||
@ -883,14 +885,14 @@ sketch002 = startSketchOn({
|
|||||||
'%s',
|
'%s',
|
||||||
async (name, { codeBefore, codeAfter, lineOfInterest, type }) => {
|
async (name, { codeBefore, codeAfter, lineOfInterest, type }) => {
|
||||||
// const lineOfInterest = 'line([-2.94, 2.7], %)'
|
// const lineOfInterest = 'line([-2.94, 2.7], %)'
|
||||||
const ast = parse(codeBefore)
|
const ast = assertParse(codeBefore)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
|
|
||||||
// deleteFromSelection
|
// deleteFromSelection
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
codeBefore.indexOf(lineOfInterest),
|
codeBefore.indexOf(lineOfInterest),
|
||||||
codeBefore.indexOf(lineOfInterest) + lineOfInterest.length,
|
codeBefore.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const artifact = { type } as Artifact
|
const artifact = { type } as Artifact
|
||||||
const newAst = await deleteFromSelection(
|
const newAst = await deleteFromSelection(
|
||||||
|
@ -66,8 +66,7 @@ export function startSketchOnDefault(
|
|||||||
let pathToNode: PathToNode = [
|
let pathToNode: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[sketchIndex, 'index'],
|
[sketchIndex, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
['0', 'index'],
|
|
||||||
['init', 'VariableDeclarator'],
|
['init', 'VariableDeclarator'],
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -94,7 +93,7 @@ export function addStartProfileAt(
|
|||||||
return new Error('variableDeclaration.init.type !== PipeExpression')
|
return new Error('variableDeclaration.init.type !== PipeExpression')
|
||||||
}
|
}
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const init = variableDeclaration.declarations[0].init
|
const init = variableDeclaration.declaration.init
|
||||||
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
||||||
createArrayExpression([
|
createArrayExpression([
|
||||||
createLiteral(roundOff(at[0])),
|
createLiteral(roundOff(at[0])),
|
||||||
@ -105,7 +104,7 @@ export function addStartProfileAt(
|
|||||||
if (init.type === 'PipeExpression') {
|
if (init.type === 'PipeExpression') {
|
||||||
init.body.splice(1, 0, startProfileAt)
|
init.body.splice(1, 0, startProfileAt)
|
||||||
} else {
|
} else {
|
||||||
variableDeclaration.declarations[0].init = createPipeExpression([
|
variableDeclaration.declaration.init = createPipeExpression([
|
||||||
init,
|
init,
|
||||||
startProfileAt,
|
startProfileAt,
|
||||||
])
|
])
|
||||||
@ -149,8 +148,7 @@ export function addSketchTo(
|
|||||||
let pathToNode: PathToNode = [
|
let pathToNode: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[sketchIndex, 'index'],
|
[sketchIndex, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
['0', 'index'],
|
|
||||||
['init', 'VariableDeclarator'],
|
['init', 'VariableDeclarator'],
|
||||||
]
|
]
|
||||||
if (axis !== 'xy') {
|
if (axis !== 'xy') {
|
||||||
@ -333,8 +331,7 @@ export function extrudeSketch(
|
|||||||
const pathToExtrudeArg: PathToNode = [
|
const pathToExtrudeArg: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[sketchIndexInBody + 1, 'index'],
|
[sketchIndexInBody + 1, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', 'VariableDeclarator'],
|
['init', 'VariableDeclarator'],
|
||||||
['arguments', 'CallExpression'],
|
['arguments', 'CallExpression'],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
@ -346,6 +343,36 @@ export function extrudeSketch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function loftSketches(
|
||||||
|
node: Node<Program>,
|
||||||
|
declarators: VariableDeclarator[]
|
||||||
|
): {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
} {
|
||||||
|
const modifiedAst = structuredClone(node)
|
||||||
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.LOFT)
|
||||||
|
const elements = declarators.map((d) => createIdentifier(d.id.name))
|
||||||
|
const loft = createCallExpressionStdLib('loft', [
|
||||||
|
createArrayExpression(elements),
|
||||||
|
])
|
||||||
|
const declaration = createVariableDeclaration(name, loft)
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
const pathToNode: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[modifiedAst.body.length - 1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function revolveSketch(
|
export function revolveSketch(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
@ -429,8 +456,7 @@ export function revolveSketch(
|
|||||||
const pathToRevolveArg: PathToNode = [
|
const pathToRevolveArg: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[sketchIndexInBody + 1, 'index'],
|
[sketchIndexInBody + 1, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', 'VariableDeclarator'],
|
['init', 'VariableDeclarator'],
|
||||||
['arguments', 'CallExpression'],
|
['arguments', 'CallExpression'],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
@ -516,8 +542,7 @@ export function sketchOnExtrudedFace(
|
|||||||
const newpathToNode: PathToNode = [
|
const newpathToNode: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[expressionIndex + 1, 'index'],
|
[expressionIndex + 1, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', 'VariableDeclarator'],
|
['init', 'VariableDeclarator'],
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -554,8 +579,7 @@ export function addOffsetPlane({
|
|||||||
const pathToNode: PathToNode = [
|
const pathToNode: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[modifiedAst.body.length - 1, 'index'],
|
[modifiedAst.body.length - 1, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
['0', 'index'],
|
|
||||||
['init', 'VariableDeclarator'],
|
['init', 'VariableDeclarator'],
|
||||||
['arguments', 'CallExpression'],
|
['arguments', 'CallExpression'],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
@ -792,8 +816,7 @@ export function createVariableDeclaration(
|
|||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
|
|
||||||
declarations: [
|
declaration: {
|
||||||
{
|
|
||||||
type: 'VariableDeclarator',
|
type: 'VariableDeclarator',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
@ -802,7 +825,6 @@ export function createVariableDeclaration(
|
|||||||
id: createIdentifier(varName),
|
id: createIdentifier(varName),
|
||||||
init,
|
init,
|
||||||
},
|
},
|
||||||
],
|
|
||||||
visibility,
|
visibility,
|
||||||
kind,
|
kind,
|
||||||
}
|
}
|
||||||
@ -1089,7 +1111,7 @@ export async function deleteFromSelection(
|
|||||||
traverse(astClone, {
|
traverse(astClone, {
|
||||||
enter: (node, path) => {
|
enter: (node, path) => {
|
||||||
if (node.type === 'VariableDeclaration') {
|
if (node.type === 'VariableDeclaration') {
|
||||||
const dec = node.declarations[0]
|
const dec = node.declaration
|
||||||
if (
|
if (
|
||||||
dec.init.type === 'CallExpression' &&
|
dec.init.type === 'CallExpression' &&
|
||||||
(dec.init.callee.name === 'extrude' ||
|
(dec.init.callee.name === 'extrude' ||
|
||||||
@ -1124,7 +1146,7 @@ export async function deleteFromSelection(
|
|||||||
enter: (node, path) => {
|
enter: (node, path) => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
if (node.type === 'VariableDeclaration') {
|
if (node.type === 'VariableDeclaration') {
|
||||||
currentVariableName = node.declarations[0].id.name
|
currentVariableName = node.declaration.id.name
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
// match startSketchOn(${extrudeNameToDelete})
|
// match startSketchOn(${extrudeNameToDelete})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
parse,
|
assertParse,
|
||||||
recast,
|
recast,
|
||||||
initPromise,
|
initPromise,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
@ -10,18 +10,21 @@ import {
|
|||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
} from '../wasm'
|
} from '../wasm'
|
||||||
import {
|
import {
|
||||||
|
EdgeTreatmentType,
|
||||||
getPathToExtrudeForSegmentSelection,
|
getPathToExtrudeForSegmentSelection,
|
||||||
hasValidFilletSelection,
|
hasValidEdgeTreatmentSelection,
|
||||||
isTagUsedInFillet,
|
isTagUsedInEdgeTreatment,
|
||||||
modifyAstWithFilletAndTag,
|
modifyAstWithEdgeTreatmentAndTag,
|
||||||
} from './addFillet'
|
FilletParameters,
|
||||||
|
ChamferParameters,
|
||||||
|
EdgeTreatmentParameters,
|
||||||
|
} from './addEdgeTreatment'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||||
import { createLiteral } from 'lang/modifyAst'
|
import { createLiteral } from 'lang/modifyAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selection, Selections } from 'lib/selections'
|
||||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||||
import { VITE_KC_DEV_TOKEN } from 'env'
|
import { VITE_KC_DEV_TOKEN } from 'env'
|
||||||
import { KclCommandValue } from 'lib/commandTypes'
|
|
||||||
import { isOverlap } from 'lib/utils'
|
import { isOverlap } from 'lib/utils'
|
||||||
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
||||||
|
|
||||||
@ -75,9 +78,10 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
|
|||||||
code: string,
|
code: string,
|
||||||
expectedExtrudeSnippet: string
|
expectedExtrudeSnippet: string
|
||||||
): CallExpression | PipeExpression | Error {
|
): CallExpression | PipeExpression | Error {
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(expectedExtrudeSnippet),
|
code.indexOf(expectedExtrudeSnippet),
|
||||||
code.indexOf(expectedExtrudeSnippet) + expectedExtrudeSnippet.length,
|
code.indexOf(expectedExtrudeSnippet) + expectedExtrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const expectedExtrudePath = getNodePathFromSourceRange(ast, extrudeRange)
|
const expectedExtrudePath = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
const expectedExtrudeNodeResult = getNodeFromPath<
|
const expectedExtrudeNodeResult = getNodeFromPath<
|
||||||
@ -106,22 +110,16 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ast
|
// ast
|
||||||
const astOrError = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(astOrError)) return new Error('AST not found')
|
|
||||||
const ast = astOrError
|
|
||||||
|
|
||||||
// selection
|
// selection
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number, boolean] = [
|
||||||
code.indexOf(selectedSegmentSnippet),
|
code.indexOf(selectedSegmentSnippet),
|
||||||
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length,
|
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const selection: Selections = {
|
const selection: Selection = {
|
||||||
graphSelections: [
|
|
||||||
{
|
|
||||||
codeRef: codeRefFromRange(segmentRange, ast),
|
codeRef: codeRefFromRange(segmentRange, ast),
|
||||||
},
|
|
||||||
],
|
|
||||||
otherSelections: [],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// executeAst and artifactGraph
|
// executeAst and artifactGraph
|
||||||
@ -253,34 +251,24 @@ extrude003 = extrude(-15, sketch003)`
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const runModifyAstCloneWithFilletAndTag = async (
|
const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||||
code: string,
|
code: string,
|
||||||
selectionSnippets: Array<string>,
|
selectionSnippets: Array<string>,
|
||||||
radiusValue: number,
|
parameters: EdgeTreatmentParameters,
|
||||||
expectedCode: string
|
expectedCode: string
|
||||||
) => {
|
) => {
|
||||||
// ast
|
// ast
|
||||||
const astOrError = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(astOrError)) {
|
|
||||||
return new Error('AST not found')
|
|
||||||
}
|
|
||||||
const ast = astOrError
|
|
||||||
|
|
||||||
// selection
|
// selection
|
||||||
const segmentRanges: Array<[number, number]> = selectionSnippets.map(
|
const segmentRanges: Array<[number, number, boolean]> = selectionSnippets.map(
|
||||||
(selectionSnippet) => [
|
(selectionSnippet) => [
|
||||||
code.indexOf(selectionSnippet),
|
code.indexOf(selectionSnippet),
|
||||||
code.indexOf(selectionSnippet) + selectionSnippet.length,
|
code.indexOf(selectionSnippet) + selectionSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
// radius
|
|
||||||
const radius: KclCommandValue = {
|
|
||||||
valueAst: createLiteral(radiusValue),
|
|
||||||
valueText: radiusValue.toString(),
|
|
||||||
valueCalculated: radiusValue.toString(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// executeAst
|
// executeAst
|
||||||
await kclManager.executeAst({ ast })
|
await kclManager.executeAst({ ast })
|
||||||
const artifactGraph = engineCommandManager.artifactGraph
|
const artifactGraph = engineCommandManager.artifactGraph
|
||||||
@ -299,8 +287,8 @@ const runModifyAstCloneWithFilletAndTag = async (
|
|||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply fillet to selection
|
// apply edge treatment to seleciton
|
||||||
const result = modifyAstWithFilletAndTag(ast, selection, radius)
|
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||||
if (err(result)) {
|
if (err(result)) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -310,8 +298,41 @@ const runModifyAstCloneWithFilletAndTag = async (
|
|||||||
|
|
||||||
expect(newCode).toContain(expectedCode)
|
expect(newCode).toContain(expectedCode)
|
||||||
}
|
}
|
||||||
describe('Testing applyFilletToSelection', () => {
|
const createFilletParameters = (radiusValue: number): FilletParameters => ({
|
||||||
it('should add a fillet to a specific segment', async () => {
|
type: EdgeTreatmentType.Fillet,
|
||||||
|
radius: {
|
||||||
|
valueAst: createLiteral(radiusValue),
|
||||||
|
valueText: radiusValue.toString(),
|
||||||
|
valueCalculated: radiusValue.toString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const createChamferParameters = (lengthValue: number): ChamferParameters => ({
|
||||||
|
type: EdgeTreatmentType.Chamfer,
|
||||||
|
length: {
|
||||||
|
valueAst: createLiteral(lengthValue),
|
||||||
|
valueText: lengthValue.toString(),
|
||||||
|
valueCalculated: lengthValue.toString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// Iterate tests over all edge treatment types
|
||||||
|
Object.values(EdgeTreatmentType).forEach(
|
||||||
|
(edgeTreatmentType: EdgeTreatmentType) => {
|
||||||
|
// create parameters based on the edge treatment type
|
||||||
|
let parameterName: string
|
||||||
|
let parameters: EdgeTreatmentParameters
|
||||||
|
if (edgeTreatmentType === EdgeTreatmentType.Fillet) {
|
||||||
|
parameterName = 'radius'
|
||||||
|
parameters = createFilletParameters(3)
|
||||||
|
} else if (edgeTreatmentType === EdgeTreatmentType.Chamfer) {
|
||||||
|
parameterName = 'length'
|
||||||
|
parameters = createChamferParameters(3)
|
||||||
|
} else {
|
||||||
|
// Handle future edge treatments
|
||||||
|
return new Error(`Unsupported edge treatment type: ${edgeTreatmentType}`)
|
||||||
|
}
|
||||||
|
// run tests
|
||||||
|
describe(`Testing modifyAstCloneWithEdgeTreatmentAndTag with ${edgeTreatmentType}s`, () => {
|
||||||
|
it(`should add a ${edgeTreatmentType} to a specific segment`, async () => {
|
||||||
const code = `sketch001 = startSketchOn('XY')
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -321,7 +342,6 @@ describe('Testing applyFilletToSelection', () => {
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)`
|
extrude001 = extrude(-15, sketch001)`
|
||||||
const segmentSnippets = ['line([0, -20], %)']
|
const segmentSnippets = ['line([0, -20], %)']
|
||||||
const radiusValue = 3
|
|
||||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -330,16 +350,16 @@ extrude001 = extrude(-15, sketch001)`
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||||
|
|
||||||
await runModifyAstCloneWithFilletAndTag(
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
code,
|
code,
|
||||||
segmentSnippets,
|
segmentSnippets,
|
||||||
radiusValue,
|
parameters,
|
||||||
expectedCode
|
expectedCode
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('should add a fillet to the sketch pipe', async () => {
|
it(`should add a ${edgeTreatmentType} to the sketch pipe`, async () => {
|
||||||
const code = `sketch001 = startSketchOn('XY')
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -349,7 +369,6 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(-15, %)`
|
|> extrude(-15, %)`
|
||||||
const segmentSnippets = ['line([0, -20], %)']
|
const segmentSnippets = ['line([0, -20], %)']
|
||||||
const radiusValue = 3
|
|
||||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -358,16 +377,16 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(-15, %)
|
|> extrude(-15, %)
|
||||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||||
|
|
||||||
await runModifyAstCloneWithFilletAndTag(
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
code,
|
code,
|
||||||
segmentSnippets,
|
segmentSnippets,
|
||||||
radiusValue,
|
parameters,
|
||||||
expectedCode
|
expectedCode
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('should add a fillet to an already tagged segment', async () => {
|
it(`should add a ${edgeTreatmentType} to an already tagged segment`, async () => {
|
||||||
const code = `sketch001 = startSketchOn('XY')
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -377,7 +396,6 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)`
|
extrude001 = extrude(-15, sketch001)`
|
||||||
const segmentSnippets = ['line([0, -20], %, $seg01)']
|
const segmentSnippets = ['line([0, -20], %, $seg01)']
|
||||||
const radiusValue = 3
|
|
||||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -386,16 +404,16 @@ extrude001 = extrude(-15, sketch001)`
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||||
|
|
||||||
await runModifyAstCloneWithFilletAndTag(
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
code,
|
code,
|
||||||
segmentSnippets,
|
segmentSnippets,
|
||||||
radiusValue,
|
parameters,
|
||||||
expectedCode
|
expectedCode
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('should add a fillet with existing tag on other segment', async () => {
|
it(`should add a ${edgeTreatmentType} with existing tag on other segment`, async () => {
|
||||||
const code = `sketch001 = startSketchOn('XY')
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %, $seg01)
|
|> line([20, 0], %, $seg01)
|
||||||
@ -405,7 +423,6 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)`
|
extrude001 = extrude(-15, sketch001)`
|
||||||
const segmentSnippets = ['line([-20, 0], %)']
|
const segmentSnippets = ['line([-20, 0], %)']
|
||||||
const radiusValue = 3
|
|
||||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %, $seg01)
|
|> line([20, 0], %, $seg01)
|
||||||
@ -414,16 +431,16 @@ extrude001 = extrude(-15, sketch001)`
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> fillet({ radius = 3, tags = [seg02] }, %)`
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||||
|
|
||||||
await runModifyAstCloneWithFilletAndTag(
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
code,
|
code,
|
||||||
segmentSnippets,
|
segmentSnippets,
|
||||||
radiusValue,
|
parameters,
|
||||||
expectedCode
|
expectedCode
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('should add a fillet with existing fillet on other segment', async () => {
|
it(`should add a ${edgeTreatmentType} with existing fillet on other segment`, async () => {
|
||||||
const code = `sketch001 = startSketchOn('XY')
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %, $seg01)
|
|> line([20, 0], %, $seg01)
|
||||||
@ -434,7 +451,6 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> fillet({ radius = 5, tags = [seg01] }, %)`
|
|> fillet({ radius = 5, tags = [seg01] }, %)`
|
||||||
const segmentSnippets = ['line([-20, 0], %)']
|
const segmentSnippets = ['line([-20, 0], %)']
|
||||||
const radiusValue = 3
|
|
||||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %, $seg01)
|
|> line([20, 0], %, $seg01)
|
||||||
@ -444,16 +460,45 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> fillet({ radius = 5, tags = [seg01] }, %)
|
|> fillet({ radius = 5, tags = [seg01] }, %)
|
||||||
|> fillet({ radius = 3, tags = [seg02] }, %)`
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||||
|
|
||||||
await runModifyAstCloneWithFilletAndTag(
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
code,
|
code,
|
||||||
segmentSnippets,
|
segmentSnippets,
|
||||||
radiusValue,
|
parameters,
|
||||||
expectedCode
|
expectedCode
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('should add a fillet to two segments of a single extrusion', async () => {
|
it(`should add a ${edgeTreatmentType} with existing chamfer on other segment`, async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %, $seg01)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
|> chamfer({ length: 5, tags: [seg01] }, %)`
|
||||||
|
const segmentSnippets = ['line([-20, 0], %)']
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %, $seg01)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg02)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
|> chamfer({ length: 5, tags: [seg01] }, %)
|
||||||
|
|> ${edgeTreatmentType}({ ${parameterName}: 3, tags: [seg02] }, %)`
|
||||||
|
|
||||||
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
|
code,
|
||||||
|
segmentSnippets,
|
||||||
|
parameters,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it(`should add a ${edgeTreatmentType} to two segments of a single extrusion`, async () => {
|
||||||
const code = `sketch001 = startSketchOn('XY')
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -463,7 +508,6 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)`
|
extrude001 = extrude(-15, sketch001)`
|
||||||
const segmentSnippets = ['line([20, 0], %)', 'line([-20, 0], %)']
|
const segmentSnippets = ['line([20, 0], %)', 'line([-20, 0], %)']
|
||||||
const radiusValue = 3
|
|
||||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %, $seg01)
|
|> line([20, 0], %, $seg01)
|
||||||
@ -472,16 +516,16 @@ extrude001 = extrude(-15, sketch001)`
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> fillet({ radius = 3, tags = [seg01, seg02] }, %)`
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01, seg02] }, %)`
|
||||||
|
|
||||||
await runModifyAstCloneWithFilletAndTag(
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
code,
|
code,
|
||||||
segmentSnippets,
|
segmentSnippets,
|
||||||
radiusValue,
|
parameters,
|
||||||
expectedCode
|
expectedCode
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('should add fillets to two bodies', async () => {
|
it(`should add ${edgeTreatmentType}s to two bodies`, async () => {
|
||||||
const code = `sketch001 = startSketchOn('XY')
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -503,7 +547,6 @@ extrude002 = extrude(-25, sketch002)` // <--- body 2
|
|||||||
'line([-20, 0], %)',
|
'line([-20, 0], %)',
|
||||||
'line([0, -15], %)',
|
'line([0, -15], %)',
|
||||||
]
|
]
|
||||||
const radiusValue = 3
|
|
||||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
|> line([20, 0], %, $seg01)
|
|> line([20, 0], %, $seg01)
|
||||||
@ -512,7 +555,7 @@ extrude002 = extrude(-25, sketch002)` // <--- body 2
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> fillet({ radius = 3, tags = [seg01, seg02] }, %)
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01, seg02] }, %)
|
||||||
sketch002 = startSketchOn('XY')
|
sketch002 = startSketchOn('XY')
|
||||||
|> startProfileAt([30, 10], %)
|
|> startProfileAt([30, 10], %)
|
||||||
|> line([15, 0], %)
|
|> line([15, 0], %)
|
||||||
@ -521,18 +564,20 @@ sketch002 = startSketchOn('XY')
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude002 = extrude(-25, sketch002)
|
extrude002 = extrude(-25, sketch002)
|
||||||
|> fillet({ radius = 3, tags = [seg03] }, %)` // <-- able to add a new one
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg03] }, %)` // <-- able to add a new one
|
||||||
|
|
||||||
await runModifyAstCloneWithFilletAndTag(
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
code,
|
code,
|
||||||
segmentSnippets,
|
segmentSnippets,
|
||||||
radiusValue,
|
parameters,
|
||||||
expectedCode
|
expectedCode
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
describe('Testing isTagUsedInFillet', () => {
|
describe('Testing isTagUsedInEdgeTreatment', () => {
|
||||||
const code = `sketch001 = startSketchOn('XZ')
|
const code = `sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.72, 4.13], %)
|
|> startProfileAt([7.72, 4.13], %)
|
||||||
|> line([7.11, 3.48], %, $seg01)
|
|> line([7.11, 3.48], %, $seg01)
|
||||||
@ -550,12 +595,12 @@ extrude001 = extrude(-5, sketch001)
|
|||||||
}, %)
|
}, %)
|
||||||
`
|
`
|
||||||
it('should correctly identify getOppositeEdge and baseEdge edges', () => {
|
it('should correctly identify getOppositeEdge and baseEdge edges', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return
|
|
||||||
const lineOfInterest = `line([7.11, 3.48], %, $seg01)`
|
const lineOfInterest = `line([7.11, 3.48], %, $seg01)`
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
if (err(pathToNode)) return
|
if (err(pathToNode)) return
|
||||||
@ -565,16 +610,16 @@ extrude001 = extrude(-5, sketch001)
|
|||||||
'CallExpression'
|
'CallExpression'
|
||||||
)
|
)
|
||||||
if (err(callExp)) return
|
if (err(callExp)) return
|
||||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||||
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
||||||
})
|
})
|
||||||
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return
|
|
||||||
const lineOfInterest = `line([-6.37, 3.88], %, $seg02)`
|
const lineOfInterest = `line([-6.37, 3.88], %, $seg02)`
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
if (err(pathToNode)) return
|
if (err(pathToNode)) return
|
||||||
@ -584,16 +629,16 @@ extrude001 = extrude(-5, sketch001)
|
|||||||
'CallExpression'
|
'CallExpression'
|
||||||
)
|
)
|
||||||
if (err(callExp)) return
|
if (err(callExp)) return
|
||||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||||
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
||||||
})
|
})
|
||||||
it('should correctly identify no edges', () => {
|
it('should correctly identify no edges', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return
|
|
||||||
const lineOfInterest = `line([-3.29, -13.85], %)`
|
const lineOfInterest = `line([-3.29, -13.85], %)`
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
if (err(pathToNode)) return
|
if (err(pathToNode)) return
|
||||||
@ -603,7 +648,7 @@ extrude001 = extrude(-5, sketch001)
|
|||||||
'CallExpression'
|
'CallExpression'
|
||||||
)
|
)
|
||||||
if (err(callExp)) return
|
if (err(callExp)) return
|
||||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||||
expect(edges).toEqual([])
|
expect(edges).toEqual([])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -614,19 +659,15 @@ describe('Testing button states', () => {
|
|||||||
segmentSnippet: string,
|
segmentSnippet: string,
|
||||||
expectedState: boolean
|
expectedState: boolean
|
||||||
) => {
|
) => {
|
||||||
// ast
|
const ast = assertParse(code)
|
||||||
const astOrError = parse(code)
|
|
||||||
if (err(astOrError)) {
|
|
||||||
return new Error('AST not found')
|
|
||||||
}
|
|
||||||
const ast = astOrError
|
|
||||||
|
|
||||||
const range: [number, number] = segmentSnippet
|
const range: [number, number, boolean] = segmentSnippet
|
||||||
? [
|
? [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
: [ast.end, ast.end] // empty line in the end of the code
|
: [ast.end, ast.end, true] // empty line in the end of the code
|
||||||
|
|
||||||
const selectionRanges: Selections = {
|
const selectionRanges: Selections = {
|
||||||
graphSelections: [
|
graphSelections: [
|
||||||
@ -638,7 +679,7 @@ describe('Testing button states', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// state
|
// state
|
||||||
const buttonState = hasValidFilletSelection({
|
const buttonState = hasValidEdgeTreatmentSelection({
|
||||||
ast,
|
ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
code,
|
code,
|
@ -29,7 +29,7 @@ import {
|
|||||||
sketchLineHelperMap,
|
sketchLineHelperMap,
|
||||||
} from '../std/sketch'
|
} from '../std/sketch'
|
||||||
import { err, trap } from 'lib/trap'
|
import { err, trap } from 'lib/trap'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selection, Selections } from 'lib/selections'
|
||||||
import { KclCommandValue } from 'lib/commandTypes'
|
import { KclCommandValue } from 'lib/commandTypes'
|
||||||
import {
|
import {
|
||||||
Artifact,
|
Artifact,
|
||||||
@ -44,32 +44,49 @@ import {
|
|||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
// Apply Fillet To Selection
|
// Edge Treatment Types
|
||||||
|
export enum EdgeTreatmentType {
|
||||||
|
Chamfer = 'chamfer',
|
||||||
|
Fillet = 'fillet',
|
||||||
|
}
|
||||||
|
|
||||||
export function applyFilletToSelection(
|
export interface ChamferParameters {
|
||||||
|
type: EdgeTreatmentType.Chamfer
|
||||||
|
length: KclCommandValue
|
||||||
|
}
|
||||||
|
export interface FilletParameters {
|
||||||
|
type: EdgeTreatmentType.Fillet
|
||||||
|
radius: KclCommandValue
|
||||||
|
}
|
||||||
|
export type EdgeTreatmentParameters = ChamferParameters | FilletParameters
|
||||||
|
|
||||||
|
// Apply Edge Treatment (Fillet or Chamfer) To Selection
|
||||||
|
export function applyEdgeTreatmentToSelection(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
selection: Selections,
|
selection: Selections,
|
||||||
radius: KclCommandValue
|
parameters: EdgeTreatmentParameters
|
||||||
): void | Error {
|
): void | Error {
|
||||||
// 1. clone and modify with fillet and tag
|
// 1. clone and modify with edge treatment and tag
|
||||||
const result = modifyAstWithFilletAndTag(ast, selection, radius)
|
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { modifiedAst, pathToFilletNode } = result
|
const { modifiedAst, pathToEdgeTreatmentNode } = result
|
||||||
|
|
||||||
// 2. update ast
|
// 2. update ast
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
updateAstAndFocus(modifiedAst, pathToFilletNode)
|
updateAstAndFocus(modifiedAst, pathToEdgeTreatmentNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function modifyAstWithFilletAndTag(
|
export function modifyAstWithEdgeTreatmentAndTag(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
selections: Selections,
|
selections: Selections,
|
||||||
radius: KclCommandValue
|
parameters: EdgeTreatmentParameters
|
||||||
): { modifiedAst: Node<Program>; pathToFilletNode: Array<PathToNode> } | Error {
|
):
|
||||||
|
| { modifiedAst: Node<Program>; pathToEdgeTreatmentNode: Array<PathToNode> }
|
||||||
|
| Error {
|
||||||
let clonedAst = structuredClone(ast)
|
let clonedAst = structuredClone(ast)
|
||||||
const clonedAstForGetExtrude = structuredClone(ast)
|
const clonedAstForGetExtrude = structuredClone(ast)
|
||||||
|
|
||||||
const astResult = insertRadiusIntoAst(clonedAst, radius)
|
const astResult = insertParametersIntoAst(clonedAst, parameters)
|
||||||
if (err(astResult)) return astResult
|
if (err(astResult)) return astResult
|
||||||
|
|
||||||
const artifactGraph = engineCommandManager.artifactGraph
|
const artifactGraph = engineCommandManager.artifactGraph
|
||||||
@ -82,14 +99,9 @@ export function modifyAstWithFilletAndTag(
|
|||||||
const lookupMap: Map<string, PathToNode> = new Map() // work around for Map key comparison
|
const lookupMap: Map<string, PathToNode> = new Map() // work around for Map key comparison
|
||||||
|
|
||||||
for (const selection of selections.graphSelections) {
|
for (const selection of selections.graphSelections) {
|
||||||
const singleSelection = {
|
|
||||||
graphSelections: [selection],
|
|
||||||
otherSelections: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = getPathToExtrudeForSegmentSelection(
|
const result = getPathToExtrudeForSegmentSelection(
|
||||||
clonedAstForGetExtrude,
|
clonedAstForGetExtrude,
|
||||||
singleSelection,
|
selection,
|
||||||
artifactGraph
|
artifactGraph
|
||||||
)
|
)
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
@ -119,21 +131,26 @@ export function modifyAstWithFilletAndTag(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Apply fillet(s) for each extrude node (body)
|
// Step 2: Apply edge treatments for each extrude node (body)
|
||||||
let pathToFilletNodes: Array<PathToNode> = []
|
let pathToEdgeTreatmentNodes: Array<PathToNode> = []
|
||||||
for (const [pathToExtrudeNode, tagInfos] of extrudeToTagsMap.entries()) {
|
for (const [pathToExtrudeNode, tagInfos] of extrudeToTagsMap.entries()) {
|
||||||
// Create a fillet expression with multiple tags
|
// Create an edge treatment expression with multiple tags
|
||||||
const radiusValue =
|
|
||||||
'variableName' in radius ? radius.variableIdentifierAst : radius.valueAst
|
|
||||||
|
|
||||||
|
// edge treatment parameter
|
||||||
|
const parameterResult = getParameterNameAndValue(parameters)
|
||||||
|
if (err(parameterResult)) return parameterResult
|
||||||
|
const { parameterName, parameterValue } = parameterResult
|
||||||
|
|
||||||
|
// tag calls
|
||||||
const tagCalls = tagInfos.map(({ tag, artifact }) => {
|
const tagCalls = tagInfos.map(({ tag, artifact }) => {
|
||||||
return getEdgeTagCall(tag, artifact)
|
return getEdgeTagCall(tag, artifact)
|
||||||
})
|
})
|
||||||
const firstTag = tagCalls[0] // can be Identifier or CallExpression (for opposite and adjacent edges)
|
const firstTag = tagCalls[0] // can be Identifier or CallExpression (for opposite and adjacent edges)
|
||||||
|
|
||||||
const filletCall = createCallExpressionStdLib('fillet', [
|
// edge treatment call
|
||||||
|
const edgeTreatmentCall = createCallExpressionStdLib(parameters.type, [
|
||||||
createObjectExpression({
|
createObjectExpression({
|
||||||
radius: radiusValue,
|
[parameterName]: parameterValue,
|
||||||
tags: createArrayExpression(tagCalls),
|
tags: createArrayExpression(tagCalls),
|
||||||
}),
|
}),
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
@ -147,64 +164,89 @@ export function modifyAstWithFilletAndTag(
|
|||||||
if (err(locatedExtrudeDeclarator)) return locatedExtrudeDeclarator
|
if (err(locatedExtrudeDeclarator)) return locatedExtrudeDeclarator
|
||||||
const { extrudeDeclarator } = locatedExtrudeDeclarator
|
const { extrudeDeclarator } = locatedExtrudeDeclarator
|
||||||
|
|
||||||
// Modify the extrude expression to include this fillet expression
|
// Modify the extrude expression to include this edge treatment expression
|
||||||
// CallExpression - no fillet
|
// CallExpression - no edge treatment
|
||||||
// PipeExpression - fillet exists or extrude in sketch pipe
|
// PipeExpression - edge treatment exists or body in sketch pipe
|
||||||
|
|
||||||
let pathToFilletNode: PathToNode = []
|
let pathToEdgeTreatmentNode: PathToNode
|
||||||
|
|
||||||
if (extrudeDeclarator.init.type === 'CallExpression') {
|
if (extrudeDeclarator.init.type === 'CallExpression') {
|
||||||
// 1. case when no fillet exists
|
// 1. case when no edge treatment exists
|
||||||
|
|
||||||
// modify ast with new fillet call by mutating the extrude node
|
// modify ast with new edge treatment call by mutating the extrude node
|
||||||
extrudeDeclarator.init = createPipeExpression([
|
extrudeDeclarator.init = createPipeExpression([
|
||||||
extrudeDeclarator.init,
|
extrudeDeclarator.init,
|
||||||
filletCall,
|
edgeTreatmentCall,
|
||||||
])
|
])
|
||||||
|
|
||||||
// get path to the fillet node
|
// get path to the edge treatment node
|
||||||
pathToFilletNode = getPathToNodeOfFilletLiteral(
|
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||||
pathToExtrudeNode,
|
pathToExtrudeNode,
|
||||||
extrudeDeclarator,
|
extrudeDeclarator,
|
||||||
firstTag
|
firstTag,
|
||||||
|
parameters
|
||||||
)
|
)
|
||||||
pathToFilletNodes.push(pathToFilletNode)
|
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||||
} else if (extrudeDeclarator.init.type === 'PipeExpression') {
|
} else if (extrudeDeclarator.init.type === 'PipeExpression') {
|
||||||
// 2. case when fillet exists or extrude in sketch pipe
|
// 2. case when edge treatment exists or extrude in sketch pipe
|
||||||
|
|
||||||
// mutate the extrude node with the new fillet call
|
// mutate the extrude node with the new edge treatment call
|
||||||
extrudeDeclarator.init.body.push(filletCall)
|
extrudeDeclarator.init.body.push(edgeTreatmentCall)
|
||||||
|
|
||||||
// get path to the fillet node
|
// get path to the edge treatment node
|
||||||
pathToFilletNode = getPathToNodeOfFilletLiteral(
|
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||||
pathToExtrudeNode,
|
pathToExtrudeNode,
|
||||||
extrudeDeclarator,
|
extrudeDeclarator,
|
||||||
firstTag
|
firstTag,
|
||||||
|
parameters
|
||||||
)
|
)
|
||||||
pathToFilletNodes.push(pathToFilletNode)
|
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||||
} else {
|
} else {
|
||||||
return new Error('Unsupported extrude type.')
|
return new Error('Unsupported extrude type.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { modifiedAst: clonedAst, pathToFilletNode: pathToFilletNodes }
|
return {
|
||||||
|
modifiedAst: clonedAst,
|
||||||
|
pathToEdgeTreatmentNode: pathToEdgeTreatmentNodes,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertRadiusIntoAst(
|
function insertParametersIntoAst(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
radius: KclCommandValue
|
parameters: EdgeTreatmentParameters
|
||||||
): { ast: Program } | Error {
|
): { ast: Program } | Error {
|
||||||
try {
|
try {
|
||||||
// Validate and update AST
|
|
||||||
if (
|
|
||||||
'variableName' in radius &&
|
|
||||||
radius.variableName &&
|
|
||||||
radius.insertIndex !== undefined
|
|
||||||
) {
|
|
||||||
const newAst = structuredClone(ast)
|
const newAst = structuredClone(ast)
|
||||||
newAst.body.splice(radius.insertIndex, 0, radius.variableDeclarationAst)
|
|
||||||
return { ast: newAst }
|
// handle radius parameter
|
||||||
|
if (
|
||||||
|
parameters.type === EdgeTreatmentType.Fillet &&
|
||||||
|
'variableName' in parameters.radius &&
|
||||||
|
parameters.radius.variableName &&
|
||||||
|
parameters.radius.insertIndex !== undefined
|
||||||
|
) {
|
||||||
|
newAst.body.splice(
|
||||||
|
parameters.radius.insertIndex,
|
||||||
|
0,
|
||||||
|
parameters.radius.variableDeclarationAst
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return { ast }
|
// handle length parameter
|
||||||
|
if (
|
||||||
|
parameters.type === EdgeTreatmentType.Chamfer &&
|
||||||
|
'variableName' in parameters.length &&
|
||||||
|
parameters.length.variableName &&
|
||||||
|
parameters.length.insertIndex !== undefined
|
||||||
|
) {
|
||||||
|
newAst.body.splice(
|
||||||
|
parameters.length.insertIndex,
|
||||||
|
0,
|
||||||
|
parameters.length.variableDeclarationAst
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle upcoming parameters here (for blend, bevel, etc.)
|
||||||
|
return { ast: newAst }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return new Error(`Failed to handle AST: ${(error as Error).message}`)
|
return new Error(`Failed to handle AST: ${(error as Error).message}`)
|
||||||
}
|
}
|
||||||
@ -212,12 +254,12 @@ function insertRadiusIntoAst(
|
|||||||
|
|
||||||
export function getPathToExtrudeForSegmentSelection(
|
export function getPathToExtrudeForSegmentSelection(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
selection: Selections,
|
selection: Selection,
|
||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
): { pathToSegmentNode: PathToNode; pathToExtrudeNode: PathToNode } | Error {
|
): { pathToSegmentNode: PathToNode; pathToExtrudeNode: PathToNode } | Error {
|
||||||
const pathToSegmentNode = getNodePathFromSourceRange(
|
const pathToSegmentNode = getNodePathFromSourceRange(
|
||||||
ast,
|
ast,
|
||||||
selection.graphSelections[0]?.codeRef?.range
|
selection.codeRef?.range
|
||||||
)
|
)
|
||||||
|
|
||||||
const varDecNode = getNodeFromPath<VariableDeclaration>(
|
const varDecNode = getNodeFromPath<VariableDeclaration>(
|
||||||
@ -226,7 +268,7 @@ export function getPathToExtrudeForSegmentSelection(
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (err(varDecNode)) return varDecNode
|
if (err(varDecNode)) return varDecNode
|
||||||
const sketchVar = varDecNode.node.declarations[0].id.name
|
const sketchVar = varDecNode.node.declaration.id.name
|
||||||
|
|
||||||
const sketch = sketchFromKclValue(
|
const sketch = sketchFromKclValue(
|
||||||
kclManager.programMemory.get(sketchVar),
|
kclManager.programMemory.get(sketchVar),
|
||||||
@ -248,10 +290,10 @@ export function getPathToExtrudeForSegmentSelection(
|
|||||||
|
|
||||||
async function updateAstAndFocus(
|
async function updateAstAndFocus(
|
||||||
modifiedAst: Node<Program>,
|
modifiedAst: Node<Program>,
|
||||||
pathToFilletNode: Array<PathToNode>
|
pathToEdgeTreatmentNode: Array<PathToNode>
|
||||||
) {
|
) {
|
||||||
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||||
focusPath: pathToFilletNode,
|
focusPath: pathToEdgeTreatmentNode,
|
||||||
})
|
})
|
||||||
|
|
||||||
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
|
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
|
||||||
@ -261,7 +303,7 @@ async function updateAstAndFocus(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mutateAstWithTagForSketchSegment(
|
export function mutateAstWithTagForSketchSegment(
|
||||||
astClone: Node<Program>,
|
astClone: Node<Program>,
|
||||||
pathToSegmentNode: PathToNode
|
pathToSegmentNode: PathToNode
|
||||||
): { modifiedAst: Program; tag: string } | Error {
|
): { modifiedAst: Program; tag: string } | Error {
|
||||||
@ -320,7 +362,7 @@ function locateExtrudeDeclarator(
|
|||||||
if (err(nodeOfExtrudeCall)) return nodeOfExtrudeCall
|
if (err(nodeOfExtrudeCall)) return nodeOfExtrudeCall
|
||||||
|
|
||||||
const { node: extrudeVarDecl } = nodeOfExtrudeCall
|
const { node: extrudeVarDecl } = nodeOfExtrudeCall
|
||||||
const extrudeDeclarator = extrudeVarDecl.declarations[0]
|
const extrudeDeclarator = extrudeVarDecl.declaration
|
||||||
if (!extrudeDeclarator) {
|
if (!extrudeDeclarator) {
|
||||||
return new Error('Extrude Declarator not found.')
|
return new Error('Extrude Declarator not found.')
|
||||||
}
|
}
|
||||||
@ -340,27 +382,38 @@ function locateExtrudeDeclarator(
|
|||||||
return { extrudeDeclarator }
|
return { extrudeDeclarator }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPathToNodeOfFilletLiteral(
|
function getPathToNodeOfEdgeTreatmentLiteral(
|
||||||
pathToExtrudeNode: PathToNode,
|
pathToExtrudeNode: PathToNode,
|
||||||
extrudeDeclarator: VariableDeclarator,
|
extrudeDeclarator: VariableDeclarator,
|
||||||
tag: Identifier | CallExpression
|
tag: Identifier | CallExpression,
|
||||||
|
parameters: EdgeTreatmentParameters
|
||||||
): PathToNode {
|
): PathToNode {
|
||||||
let pathToFilletObj: PathToNode = []
|
let pathToEdgeTreatmentObj: PathToNode = []
|
||||||
let inFillet = false
|
let inEdgeTreatment = false
|
||||||
|
|
||||||
traverse(extrudeDeclarator.init, {
|
traverse(extrudeDeclarator.init, {
|
||||||
enter(node, path) {
|
enter(node, path) {
|
||||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
if (
|
||||||
inFillet = true
|
node.type === 'CallExpression' &&
|
||||||
|
node.callee.name === parameters.type
|
||||||
|
) {
|
||||||
|
inEdgeTreatment = true
|
||||||
}
|
}
|
||||||
if (inFillet && node.type === 'ObjectExpression') {
|
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||||
if (!hasTag(node, tag)) return false
|
if (!hasTag(node, tag)) return false
|
||||||
pathToFilletObj = getPathToRadiusLiteral(node, path)
|
pathToEdgeTreatmentObj = getPathToEdgeTreatmentParameterLiteral(
|
||||||
|
node,
|
||||||
|
path,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leave(node) {
|
leave(node) {
|
||||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
if (
|
||||||
inFillet = false
|
node.type === 'CallExpression' &&
|
||||||
|
node.callee.name === parameters.type
|
||||||
|
) {
|
||||||
|
inEdgeTreatment = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -375,7 +428,7 @@ function getPathToNodeOfFilletLiteral(
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
...pathToExtrudeNode.slice(0, indexOfPipeExpression),
|
...pathToExtrudeNode.slice(0, indexOfPipeExpression),
|
||||||
...pathToFilletObj,
|
...pathToEdgeTreatmentObj,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,23 +461,62 @@ function hasTag(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPathToRadiusLiteral(node: ObjectExpression, path: any): PathToNode {
|
function getPathToEdgeTreatmentParameterLiteral(
|
||||||
let pathToFilletObj = path
|
node: ObjectExpression,
|
||||||
|
path: any,
|
||||||
|
parameters: EdgeTreatmentParameters
|
||||||
|
): PathToNode {
|
||||||
|
let pathToEdgeTreatmentObj = path
|
||||||
|
const parameterResult = getParameterNameAndValue(parameters)
|
||||||
|
if (err(parameterResult)) return pathToEdgeTreatmentObj
|
||||||
|
const { parameterName } = parameterResult
|
||||||
|
|
||||||
node.properties.forEach((prop, index) => {
|
node.properties.forEach((prop, index) => {
|
||||||
if (prop.key.name === 'radius') {
|
if (prop.key.name === parameterName) {
|
||||||
pathToFilletObj.push(
|
pathToEdgeTreatmentObj.push(
|
||||||
['properties', 'ObjectExpression'],
|
['properties', 'ObjectExpression'],
|
||||||
[index, 'index'],
|
[index, 'index'],
|
||||||
['value', 'Property']
|
['value', 'Property']
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return pathToFilletObj
|
return pathToEdgeTreatmentObj
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParameterNameAndValue(
|
||||||
|
parameters: EdgeTreatmentParameters
|
||||||
|
): { parameterName: string; parameterValue: Expr } | Error {
|
||||||
|
if (parameters.type === EdgeTreatmentType.Fillet) {
|
||||||
|
const parameterValue =
|
||||||
|
'variableName' in parameters.radius
|
||||||
|
? parameters.radius.variableIdentifierAst
|
||||||
|
: parameters.radius.valueAst
|
||||||
|
return { parameterName: 'radius', parameterValue }
|
||||||
|
} else if (parameters.type === EdgeTreatmentType.Chamfer) {
|
||||||
|
const parameterValue =
|
||||||
|
'variableName' in parameters.length
|
||||||
|
? parameters.length.variableIdentifierAst
|
||||||
|
: parameters.length.valueAst
|
||||||
|
return { parameterName: 'length', parameterValue }
|
||||||
|
} else {
|
||||||
|
return new Error('Unsupported edge treatment type}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type Guards
|
||||||
|
function isEdgeTreatmentType(name: string): name is EdgeTreatmentType {
|
||||||
|
return name === EdgeTreatmentType.Chamfer || name === EdgeTreatmentType.Fillet
|
||||||
|
}
|
||||||
|
function isEdgeType(name: string): name is EdgeTypes {
|
||||||
|
return (
|
||||||
|
name === 'getNextAdjacentEdge' ||
|
||||||
|
name === 'getPreviousAdjacentEdge' ||
|
||||||
|
name === 'getOppositeEdge'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Button states
|
// Button states
|
||||||
|
export const hasValidEdgeTreatmentSelection = ({
|
||||||
export const hasValidFilletSelection = ({
|
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
ast,
|
ast,
|
||||||
code,
|
code,
|
||||||
@ -433,11 +525,14 @@ export const hasValidFilletSelection = ({
|
|||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
code: string
|
code: string
|
||||||
}) => {
|
}) => {
|
||||||
// check if there is anything filletable in the scene
|
// check if there is anything valid for the edge treatment in the scene
|
||||||
let extrudeExists = false
|
let extrudeExists = false
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
enter(node) {
|
enter(node) {
|
||||||
if (node.type === 'CallExpression' && node.callee.name === 'extrude') {
|
if (
|
||||||
|
node.type === 'CallExpression' &&
|
||||||
|
(node.callee.name === 'extrude' || node.callee.name === 'revolve')
|
||||||
|
) {
|
||||||
extrudeExists = true
|
extrudeExists = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -494,32 +589,39 @@ export const hasValidFilletSelection = ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// check if tag is used in fillet
|
// check if tag is used in edge treatment
|
||||||
if (tagExists && selection.artifact) {
|
if (tagExists && selection.artifact) {
|
||||||
// create tag call
|
// create tag call
|
||||||
let tagCall: Expr = getEdgeTagCall(tag, selection.artifact)
|
let tagCall: Expr = getEdgeTagCall(tag, selection.artifact)
|
||||||
|
|
||||||
// check if tag is used in fillet
|
// check if tag is used in edge treatment
|
||||||
let inFillet = false
|
let inEdgeTreatment = false
|
||||||
let tagUsedInFillet = false
|
let tagUsedInEdgeTreatment = false
|
||||||
|
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
enter(node) {
|
enter(node) {
|
||||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
if (
|
||||||
inFillet = true
|
node.type === 'CallExpression' &&
|
||||||
|
isEdgeTreatmentType(node.callee.name)
|
||||||
|
) {
|
||||||
|
inEdgeTreatment = true
|
||||||
}
|
}
|
||||||
if (inFillet && node.type === 'ObjectExpression') {
|
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||||
if (hasTag(node, tagCall)) {
|
if (hasTag(node, tagCall)) {
|
||||||
tagUsedInFillet = true
|
tagUsedInEdgeTreatment = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leave(node) {
|
leave(node) {
|
||||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
if (
|
||||||
inFillet = false
|
node.type === 'CallExpression' &&
|
||||||
|
isEdgeTreatmentType(node.callee.name)
|
||||||
|
) {
|
||||||
|
inEdgeTreatment = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (tagUsedInFillet) {
|
if (tagUsedInEdgeTreatment) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -533,7 +635,7 @@ type EdgeTypes =
|
|||||||
| 'getPreviousAdjacentEdge'
|
| 'getPreviousAdjacentEdge'
|
||||||
| 'getOppositeEdge'
|
| 'getOppositeEdge'
|
||||||
|
|
||||||
export const isTagUsedInFillet = ({
|
export const isTagUsedInEdgeTreatment = ({
|
||||||
ast,
|
ast,
|
||||||
callExp,
|
callExp,
|
||||||
}: {
|
}: {
|
||||||
@ -543,16 +645,21 @@ export const isTagUsedInFillet = ({
|
|||||||
const tag = getTagFromCallExpression(callExp)
|
const tag = getTagFromCallExpression(callExp)
|
||||||
if (err(tag)) return []
|
if (err(tag)) return []
|
||||||
|
|
||||||
let inFillet = false
|
let inEdgeTreatment = false
|
||||||
let inObj = false
|
let inObj = false
|
||||||
let inTagHelper: EdgeTypes | '' = ''
|
let inTagHelper: EdgeTypes | '' = ''
|
||||||
const edges: Array<EdgeTypes> = []
|
const edges: Array<EdgeTypes> = []
|
||||||
|
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
enter: (node) => {
|
enter: (node) => {
|
||||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
// Check if we are entering an edge treatment call
|
||||||
inFillet = true
|
if (
|
||||||
|
node.type === 'CallExpression' &&
|
||||||
|
isEdgeTreatmentType(node.callee.name)
|
||||||
|
) {
|
||||||
|
inEdgeTreatment = true
|
||||||
}
|
}
|
||||||
if (inFillet && node.type === 'ObjectExpression') {
|
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||||
node.properties.forEach((prop) => {
|
node.properties.forEach((prop) => {
|
||||||
if (
|
if (
|
||||||
prop.key.name === 'tags' &&
|
prop.key.name === 'tags' &&
|
||||||
@ -564,17 +671,15 @@ export const isTagUsedInFillet = ({
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
inObj &&
|
inObj &&
|
||||||
inFillet &&
|
inEdgeTreatment &&
|
||||||
node.type === 'CallExpression' &&
|
node.type === 'CallExpression' &&
|
||||||
(node.callee.name === 'getOppositeEdge' ||
|
isEdgeType(node.callee.name)
|
||||||
node.callee.name === 'getNextAdjacentEdge' ||
|
|
||||||
node.callee.name === 'getPreviousAdjacentEdge')
|
|
||||||
) {
|
) {
|
||||||
inTagHelper = node.callee.name
|
inTagHelper = node.callee.name
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
inObj &&
|
inObj &&
|
||||||
inFillet &&
|
inEdgeTreatment &&
|
||||||
!inTagHelper &&
|
!inTagHelper &&
|
||||||
node.type === 'Identifier' &&
|
node.type === 'Identifier' &&
|
||||||
node.name === tag
|
node.name === tag
|
||||||
@ -583,7 +688,7 @@ export const isTagUsedInFillet = ({
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
inObj &&
|
inObj &&
|
||||||
inFillet &&
|
inEdgeTreatment &&
|
||||||
inTagHelper &&
|
inTagHelper &&
|
||||||
node.type === 'Identifier' &&
|
node.type === 'Identifier' &&
|
||||||
node.name === tag
|
node.name === tag
|
||||||
@ -592,10 +697,13 @@ export const isTagUsedInFillet = ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
leave: (node) => {
|
leave: (node) => {
|
||||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
if (
|
||||||
inFillet = false
|
node.type === 'CallExpression' &&
|
||||||
|
isEdgeTreatmentType(node.callee.name)
|
||||||
|
) {
|
||||||
|
inEdgeTreatment = false
|
||||||
}
|
}
|
||||||
if (inFillet && node.type === 'ObjectExpression') {
|
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||||
node.properties.forEach((prop) => {
|
node.properties.forEach((prop) => {
|
||||||
if (
|
if (
|
||||||
prop.key.name === 'tags' &&
|
prop.key.name === 'tags' &&
|
||||||
@ -607,11 +715,9 @@ export const isTagUsedInFillet = ({
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
inObj &&
|
inObj &&
|
||||||
inFillet &&
|
inEdgeTreatment &&
|
||||||
node.type === 'CallExpression' &&
|
node.type === 'CallExpression' &&
|
||||||
(node.callee.name === 'getOppositeEdge' ||
|
isEdgeType(node.callee.name)
|
||||||
node.callee.name === 'getNextAdjacentEdge' ||
|
|
||||||
node.callee.name === 'getPreviousAdjacentEdge')
|
|
||||||
) {
|
) {
|
||||||
inTagHelper = ''
|
inTagHelper = ''
|
||||||
}
|
}
|
124
src/lang/modifyAst/addShell.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { ArtifactGraph } from 'lang/std/artifactGraph'
|
||||||
|
import { Selections } from 'lib/selections'
|
||||||
|
import { Expr } from 'wasm-lib/kcl/bindings/Expr'
|
||||||
|
import { Program } from 'wasm-lib/kcl/bindings/Program'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
import { PathToNode, VariableDeclarator } from 'lang/wasm'
|
||||||
|
import {
|
||||||
|
getPathToExtrudeForSegmentSelection,
|
||||||
|
mutateAstWithTagForSketchSegment,
|
||||||
|
} from './addEdgeTreatment'
|
||||||
|
import { getNodeFromPath } from 'lang/queryAst'
|
||||||
|
import { err } from 'lib/trap'
|
||||||
|
import {
|
||||||
|
createLiteral,
|
||||||
|
createIdentifier,
|
||||||
|
findUniqueName,
|
||||||
|
createCallExpressionStdLib,
|
||||||
|
createObjectExpression,
|
||||||
|
createArrayExpression,
|
||||||
|
createVariableDeclaration,
|
||||||
|
} from 'lang/modifyAst'
|
||||||
|
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
||||||
|
|
||||||
|
export function addShell({
|
||||||
|
node,
|
||||||
|
selection,
|
||||||
|
artifactGraph,
|
||||||
|
thickness,
|
||||||
|
}: {
|
||||||
|
node: Node<Program>
|
||||||
|
selection: Selections
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
thickness: Expr
|
||||||
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
const modifiedAst = structuredClone(node)
|
||||||
|
|
||||||
|
// Look up the corresponding extrude
|
||||||
|
const clonedAstForGetExtrude = structuredClone(modifiedAst)
|
||||||
|
|
||||||
|
const expressions: Expr[] = []
|
||||||
|
let pathToExtrudeNode: PathToNode | undefined = undefined
|
||||||
|
for (const graphSelection of selection.graphSelections) {
|
||||||
|
const extrudeLookupResult = getPathToExtrudeForSegmentSelection(
|
||||||
|
clonedAstForGetExtrude,
|
||||||
|
graphSelection,
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(extrudeLookupResult)) {
|
||||||
|
return new Error("Couldn't find extrude")
|
||||||
|
}
|
||||||
|
|
||||||
|
pathToExtrudeNode = extrudeLookupResult.pathToExtrudeNode
|
||||||
|
// Get the sketch ref from the selection
|
||||||
|
// TODO: this assumes the segment is piped directly from the sketch, with no intermediate `VariableDeclarator` between.
|
||||||
|
// We must find a technique for these situations that is robust to intermediate declarations
|
||||||
|
const sketchNode = getNodeFromPath<VariableDeclarator>(
|
||||||
|
modifiedAst,
|
||||||
|
graphSelection.codeRef.pathToNode,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (err(sketchNode)) {
|
||||||
|
return sketchNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedArtifact = graphSelection.artifact
|
||||||
|
if (!selectedArtifact) {
|
||||||
|
return new Error('Bad artifact')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check on the selection, and handle the wall vs cap casees
|
||||||
|
let expr: Expr
|
||||||
|
if (selectedArtifact.type === 'cap') {
|
||||||
|
expr = createLiteral(selectedArtifact.subType)
|
||||||
|
} else if (selectedArtifact.type === 'wall') {
|
||||||
|
const tagResult = mutateAstWithTagForSketchSegment(
|
||||||
|
modifiedAst,
|
||||||
|
extrudeLookupResult.pathToSegmentNode
|
||||||
|
)
|
||||||
|
if (err(tagResult)) return tagResult
|
||||||
|
const { tag } = tagResult
|
||||||
|
expr = createIdentifier(tag)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expressions.push(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pathToExtrudeNode) return new Error('No extrude found')
|
||||||
|
|
||||||
|
const extrudeNode = getNodeFromPath<VariableDeclarator>(
|
||||||
|
modifiedAst,
|
||||||
|
pathToExtrudeNode,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (err(extrudeNode)) {
|
||||||
|
return extrudeNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SHELL)
|
||||||
|
const shell = createCallExpressionStdLib('shell', [
|
||||||
|
createObjectExpression({
|
||||||
|
faces: createArrayExpression(expressions),
|
||||||
|
thickness,
|
||||||
|
}),
|
||||||
|
createIdentifier(extrudeNode.node.id.name),
|
||||||
|
])
|
||||||
|
const declaration = createVariableDeclaration(name, shell)
|
||||||
|
|
||||||
|
// TODO: check if we should append at the end like here or right after the extrude
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
const pathToNode: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[modifiedAst.body.length - 1, 'index'],
|
||||||
|
['declarations', 'VariableDeclaration'],
|
||||||
|
['0', 'index'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,10 @@
|
|||||||
import { parse, recast, initPromise, PathToNode, Identifier } from './wasm'
|
import {
|
||||||
|
assertParse,
|
||||||
|
recast,
|
||||||
|
initPromise,
|
||||||
|
PathToNode,
|
||||||
|
Identifier,
|
||||||
|
} from './wasm'
|
||||||
import {
|
import {
|
||||||
findAllPreviousVariables,
|
findAllPreviousVariables,
|
||||||
isNodeSafeToReplace,
|
isNodeSafeToReplace,
|
||||||
@ -11,6 +17,7 @@ import {
|
|||||||
doesSceneHaveSweepableSketch,
|
doesSceneHaveSweepableSketch,
|
||||||
traverse,
|
traverse,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
|
doesSceneHaveExtrudedSketch,
|
||||||
} from './queryAst'
|
} from './queryAst'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import {
|
import {
|
||||||
@ -45,14 +52,13 @@ part001 = startSketchOn('XY')
|
|||||||
variableBelowShouldNotBeIncluded = 3
|
variableBelowShouldNotBeIncluded = 3
|
||||||
`
|
`
|
||||||
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
|
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
|
|
||||||
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[rangeStart, rangeStart]
|
[rangeStart, rangeStart, true]
|
||||||
)
|
)
|
||||||
expect(variables).toEqual([
|
expect(variables).toEqual([
|
||||||
{ key: 'baseThick', value: 1 },
|
{ key: 'baseThick', value: 1 },
|
||||||
@ -80,10 +86,9 @@ describe('testing argIsNotIdentifier', () => {
|
|||||||
yo = 5 + 6
|
yo = 5 + 6
|
||||||
yo2 = hmm([identifierGuy + 5])`
|
yo2 = hmm([identifierGuy + 5])`
|
||||||
it('find a safe binaryExpression', () => {
|
it('find a safe binaryExpression', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('100 + 100') + 2
|
const rangeStart = code.indexOf('100 + 100') + 2
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('BinaryExpression')
|
expect(result.value?.type).toBe('BinaryExpression')
|
||||||
@ -94,20 +99,18 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('find a safe Identifier', () => {
|
it('find a safe Identifier', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('abc')
|
const rangeStart = code.indexOf('abc')
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('Identifier')
|
expect(result.value?.type).toBe('Identifier')
|
||||||
expect(code.slice(result.value.start, result.value.end)).toBe('abc')
|
expect(code.slice(result.value.start, result.value.end)).toBe('abc')
|
||||||
})
|
})
|
||||||
it('find a safe CallExpression', () => {
|
it('find a safe CallExpression', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('def')
|
const rangeStart = code.indexOf('def')
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('CallExpression')
|
expect(result.value?.type).toBe('CallExpression')
|
||||||
@ -118,10 +121,9 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('find an UNsafe CallExpression, as it has a PipeSubstitution', () => {
|
it('find an UNsafe CallExpression, as it has a PipeSubstitution', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('ghi')
|
const rangeStart = code.indexOf('ghi')
|
||||||
const range: [number, number] = [rangeStart, rangeStart]
|
const range: [number, number, boolean] = [rangeStart, rangeStart, true]
|
||||||
const result = isNodeSafeToReplace(ast, range)
|
const result = isNodeSafeToReplace(ast, range)
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(false)
|
expect(result.isSafe).toBe(false)
|
||||||
@ -129,10 +131,9 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(code.slice(result.value.start, result.value.end)).toBe('ghi(%)')
|
expect(code.slice(result.value.start, result.value.end)).toBe('ghi(%)')
|
||||||
})
|
})
|
||||||
it('find an UNsafe Identifier, as it is a callee', () => {
|
it('find an UNsafe Identifier, as it is a callee', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('ine([2.8,')
|
const rangeStart = code.indexOf('ine([2.8,')
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(false)
|
expect(result.isSafe).toBe(false)
|
||||||
expect(result.value?.type).toBe('CallExpression')
|
expect(result.value?.type).toBe('CallExpression')
|
||||||
@ -141,10 +142,9 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
it("find a safe BinaryExpression that's assigned to a variable", () => {
|
it("find a safe BinaryExpression that's assigned to a variable", () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('5 + 6') + 1
|
const rangeStart = code.indexOf('5 + 6') + 1
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('BinaryExpression')
|
expect(result.value?.type).toBe('BinaryExpression')
|
||||||
@ -155,10 +155,9 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(outCode).toContain(`yo = replaceName`)
|
expect(outCode).toContain(`yo = replaceName`)
|
||||||
})
|
})
|
||||||
it('find a safe BinaryExpression that has a CallExpression within', () => {
|
it('find a safe BinaryExpression that has a CallExpression within', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('jkl') + 1
|
const rangeStart = code.indexOf('jkl') + 1
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('BinaryExpression')
|
expect(result.value?.type).toBe('BinaryExpression')
|
||||||
@ -172,11 +171,10 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('find a safe BinaryExpression within a CallExpression', () => {
|
it('find a safe BinaryExpression within a CallExpression', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const rangeStart = code.indexOf('identifierGuy') + 1
|
const rangeStart = code.indexOf('identifierGuy') + 1
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
|
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
@ -223,15 +221,17 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
it('finds the second line when cursor is put at the end', () => {
|
it('finds the second line when cursor is put at the end', () => {
|
||||||
const searchLn = `line([0.94, 2.61], %)`
|
const searchLn = `line([0.94, 2.61], %)`
|
||||||
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', ''],
|
['init', ''],
|
||||||
['body', 'PipeExpression'],
|
['body', 'PipeExpression'],
|
||||||
[2, 'index'],
|
[2, 'index'],
|
||||||
@ -240,15 +240,17 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
it('finds the last line when cursor is put at the end', () => {
|
it('finds the last line when cursor is put at the end', () => {
|
||||||
const searchLn = `line([-0.21, -1.4], %)`
|
const searchLn = `line([-0.21, -1.4], %)`
|
||||||
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
const expected = [
|
const expected = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', ''],
|
['init', ''],
|
||||||
['body', 'PipeExpression'],
|
['body', 'PipeExpression'],
|
||||||
[3, 'index'],
|
[3, 'index'],
|
||||||
@ -259,12 +261,14 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
const startResult = getNodePathFromSourceRange(ast, [
|
const startResult = getNodePathFromSourceRange(ast, [
|
||||||
startSourceIndex,
|
startSourceIndex,
|
||||||
startSourceIndex,
|
startSourceIndex,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
expect(startResult).toEqual([...expected, ['callee', 'CallExpression']])
|
expect(startResult).toEqual([...expected, ['callee', 'CallExpression']])
|
||||||
// expect similar result when whole line is selected
|
// expect similar result when whole line is selected
|
||||||
const selectWholeThing = getNodePathFromSourceRange(ast, [
|
const selectWholeThing = getNodePathFromSourceRange(ast, [
|
||||||
startSourceIndex,
|
startSourceIndex,
|
||||||
sourceIndex,
|
sourceIndex,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
expect(selectWholeThing).toEqual(expected)
|
expect(selectWholeThing).toEqual(expected)
|
||||||
})
|
})
|
||||||
@ -278,15 +282,17 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
}`
|
}`
|
||||||
const searchLn = `x > y`
|
const searchLn = `x > y`
|
||||||
const sourceIndex = code.indexOf(searchLn)
|
const sourceIndex = code.indexOf(searchLn)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[1, 'index'],
|
[1, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', ''],
|
['init', ''],
|
||||||
['cond', 'IfExpression'],
|
['cond', 'IfExpression'],
|
||||||
['left', 'BinaryExpression'],
|
['left', 'BinaryExpression'],
|
||||||
@ -306,15 +312,17 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
}`
|
}`
|
||||||
const searchLn = `x + 1`
|
const searchLn = `x + 1`
|
||||||
const sourceIndex = code.indexOf(searchLn)
|
const sourceIndex = code.indexOf(searchLn)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[1, 'index'],
|
[1, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
|
||||||
['init', ''],
|
['init', ''],
|
||||||
['then_val', 'IfExpression'],
|
['then_val', 'IfExpression'],
|
||||||
['body', 'IfExpression'],
|
['body', 'IfExpression'],
|
||||||
@ -332,14 +340,18 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
const code = `import foo, bar as baz from 'thing.kcl'`
|
const code = `import foo, bar as baz from 'thing.kcl'`
|
||||||
const searchLn = `bar`
|
const searchLn = `bar`
|
||||||
const sourceIndex = code.indexOf(searchLn)
|
const sourceIndex = code.indexOf(searchLn)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
['items', 'ImportStatement'],
|
['selector', 'ImportStatement'],
|
||||||
|
['items', 'ImportSelector'],
|
||||||
[1, 'index'],
|
[1, 'index'],
|
||||||
['name', 'ImportItem'],
|
['name', 'ImportItem'],
|
||||||
])
|
])
|
||||||
@ -360,14 +372,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> angledLine([-175, segLen(seg01)], %)
|
|> angledLine([-175, segLen(seg01)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = doesPipeHaveCallExp({
|
const result = doesPipeHaveCallExp({
|
||||||
calleeName: 'close',
|
calleeName: 'close',
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(result).toEqual(true)
|
expect(result).toEqual(true)
|
||||||
@ -382,14 +393,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = doesPipeHaveCallExp({
|
const result = doesPipeHaveCallExp({
|
||||||
calleeName: 'extrude',
|
calleeName: 'extrude',
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(result).toEqual(true)
|
expect(result).toEqual(true)
|
||||||
@ -402,28 +412,26 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> line([-3.22, -7.36], %)
|
|> line([-3.22, -7.36], %)
|
||||||
|> angledLine([-175, segLen(seg01)], %)
|
|> angledLine([-175, segLen(seg01)], %)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = doesPipeHaveCallExp({
|
const result = doesPipeHaveCallExp({
|
||||||
calleeName: 'close',
|
calleeName: 'close',
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(result).toEqual(false)
|
expect(result).toEqual(false)
|
||||||
})
|
})
|
||||||
it('returns false if not a pipe', () => {
|
it('returns false if not a pipe', () => {
|
||||||
const exampleCode = `length001 = 2`
|
const exampleCode = `length001 = 2`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = doesPipeHaveCallExp({
|
const result = doesPipeHaveCallExp({
|
||||||
calleeName: 'close',
|
calleeName: 'close',
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([9, 10], ast),
|
codeRef: codeRefFromRange([9, 10, true], ast),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(result).toEqual(false)
|
expect(result).toEqual(false)
|
||||||
@ -438,14 +446,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> angledLine([-35, length001], %)
|
|> angledLine([-35, length001], %)
|
||||||
|> line([-3.22, -7.36], %)
|
|> line([-3.22, -7.36], %)
|
||||||
|> angledLine([-175, segLen(seg01)], %)`
|
|> angledLine([-175, segLen(seg01)], %)`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const result = hasExtrudeSketch({
|
const result = hasExtrudeSketch({
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
programMemory: execState.memory,
|
||||||
})
|
})
|
||||||
@ -459,14 +466,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> line([-3.22, -7.36], %)
|
|> line([-3.22, -7.36], %)
|
||||||
|> angledLine([-175, segLen(seg01)], %)
|
|> angledLine([-175, segLen(seg01)], %)
|
||||||
|> extrude(1, %)`
|
|> extrude(1, %)`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const result = hasExtrudeSketch({
|
const result = hasExtrudeSketch({
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
programMemory: execState.memory,
|
||||||
})
|
})
|
||||||
@ -474,14 +480,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
})
|
})
|
||||||
it('finds nothing', async () => {
|
it('finds nothing', async () => {
|
||||||
const exampleCode = `length001 = 2`
|
const exampleCode = `length001 = 2`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const result = hasExtrudeSketch({
|
const result = hasExtrudeSketch({
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([10, 11], ast),
|
codeRef: codeRefFromRange([10, 11, true], ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
programMemory: execState.memory,
|
||||||
})
|
})
|
||||||
@ -498,8 +503,7 @@ describe('Testing findUsesOfTagInPipe', () => {
|
|||||||
|> line([306.21, 198.87], %)
|
|> line([306.21, 198.87], %)
|
||||||
|> angledLine([65, segLen(seg01)], %)`
|
|> angledLine([65, segLen(seg01)], %)`
|
||||||
it('finds the current segment', async () => {
|
it('finds the current segment', async () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const lineOfInterest = `198.85], %, $seg01`
|
const lineOfInterest = `198.85], %, $seg01`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
@ -507,6 +511,7 @@ describe('Testing findUsesOfTagInPipe', () => {
|
|||||||
const pathToNode = getNodePathFromSourceRange(ast, [
|
const pathToNode = getNodePathFromSourceRange(ast, [
|
||||||
characterIndex,
|
characterIndex,
|
||||||
characterIndex,
|
characterIndex,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
const result = findUsesOfTagInPipe(ast, pathToNode)
|
const result = findUsesOfTagInPipe(ast, pathToNode)
|
||||||
expect(result).toHaveLength(2)
|
expect(result).toHaveLength(2)
|
||||||
@ -515,8 +520,7 @@ describe('Testing findUsesOfTagInPipe', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('find no tag if line has no tag', () => {
|
it('find no tag if line has no tag', () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const lineOfInterest = `line([306.21, 198.82], %)`
|
const lineOfInterest = `line([306.21, 198.82], %)`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
@ -524,6 +528,7 @@ describe('Testing findUsesOfTagInPipe', () => {
|
|||||||
const pathToNode = getNodePathFromSourceRange(ast, [
|
const pathToNode = getNodePathFromSourceRange(ast, [
|
||||||
characterIndex,
|
characterIndex,
|
||||||
characterIndex,
|
characterIndex,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
const result = findUsesOfTagInPipe(ast, pathToNode)
|
const result = findUsesOfTagInPipe(ast, pathToNode)
|
||||||
expect(result).toHaveLength(0)
|
expect(result).toHaveLength(0)
|
||||||
@ -564,42 +569,39 @@ sketch003 = startSketchOn(extrude001, 'END')
|
|||||||
|> extrude(3.14, %)
|
|> extrude(3.14, %)
|
||||||
`
|
`
|
||||||
it('identifies sketch001 pipe as extruded (extrusion after pipe)', async () => {
|
it('identifies sketch001 pipe as extruded (extrusion after pipe)', async () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const lineOfInterest = `line([4.99, -0.46], %, $seg01)`
|
const lineOfInterest = `line([4.99, -0.46], %, $seg01)`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
||||||
const extruded = hasSketchPipeBeenExtruded(
|
const extruded = hasSketchPipeBeenExtruded(
|
||||||
{
|
{
|
||||||
codeRef: codeRefFromRange([characterIndex, characterIndex], ast),
|
codeRef: codeRefFromRange([characterIndex, characterIndex, true], ast),
|
||||||
},
|
},
|
||||||
ast
|
ast
|
||||||
)
|
)
|
||||||
expect(extruded).toBeTruthy()
|
expect(extruded).toBeTruthy()
|
||||||
})
|
})
|
||||||
it('identifies sketch002 pipe as not extruded', async () => {
|
it('identifies sketch002 pipe as not extruded', async () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const lineOfInterest = `line([2.45, -0.2], %)`
|
const lineOfInterest = `line([2.45, -0.2], %)`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
||||||
const extruded = hasSketchPipeBeenExtruded(
|
const extruded = hasSketchPipeBeenExtruded(
|
||||||
{
|
{
|
||||||
codeRef: codeRefFromRange([characterIndex, characterIndex], ast),
|
codeRef: codeRefFromRange([characterIndex, characterIndex, true], ast),
|
||||||
},
|
},
|
||||||
ast
|
ast
|
||||||
)
|
)
|
||||||
expect(extruded).toBeFalsy()
|
expect(extruded).toBeFalsy()
|
||||||
})
|
})
|
||||||
it('identifies sketch003 pipe as extruded (extrusion within pipe)', async () => {
|
it('identifies sketch003 pipe as extruded (extrusion within pipe)', async () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const lineOfInterest = `|> line([3.12, 1.74], %)`
|
const lineOfInterest = `|> line([3.12, 1.74], %)`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
||||||
const extruded = hasSketchPipeBeenExtruded(
|
const extruded = hasSketchPipeBeenExtruded(
|
||||||
{
|
{
|
||||||
codeRef: codeRefFromRange([characterIndex, characterIndex], ast),
|
codeRef: codeRefFromRange([characterIndex, characterIndex, true], ast),
|
||||||
},
|
},
|
||||||
ast
|
ast
|
||||||
)
|
)
|
||||||
@ -623,11 +625,21 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const extrudable = doesSceneHaveSweepableSketch(ast)
|
const extrudable = doesSceneHaveSweepableSketch(ast)
|
||||||
expect(extrudable).toBeTruthy()
|
expect(extrudable).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
it('finds sketch001 and sketch002 pipes to be lofted', async () => {
|
||||||
|
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 2)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 3 }, %)
|
||||||
|
`
|
||||||
|
const ast = assertParse(exampleCode)
|
||||||
|
const extrudable = doesSceneHaveSweepableSketch(ast, 2)
|
||||||
|
expect(extrudable).toBeTruthy()
|
||||||
|
})
|
||||||
it('find sketch002 NOT pipe to be extruded', async () => {
|
it('find sketch002 NOT pipe to be extruded', async () => {
|
||||||
const exampleCode = `sketch001 = startSketchOn('XZ')
|
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([3.29, 7.86], %)
|
|> startProfileAt([3.29, 7.86], %)
|
||||||
@ -636,10 +648,41 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
|||||||
|> line([-17.67, 0.85], %)
|
|> line([-17.67, 0.85], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(10, sketch001)
|
extrude001 = extrude(10, sketch001)
|
||||||
|
`
|
||||||
|
const ast = assertParse(exampleCode)
|
||||||
|
const extrudable = doesSceneHaveSweepableSketch(ast)
|
||||||
|
expect(extrudable).toBeFalsy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing doesSceneHaveExtrudedSketch', () => {
|
||||||
|
it('finds extruded sketch as variable', async () => {
|
||||||
|
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||||
|
extrude001 = extrude(1, sketch001)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = parse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
const extrudable = doesSceneHaveSweepableSketch(ast)
|
const extrudable = doesSceneHaveExtrudedSketch(ast)
|
||||||
|
expect(extrudable).toBeTruthy()
|
||||||
|
})
|
||||||
|
it('finds extruded sketch in pipe', async () => {
|
||||||
|
const exampleCode = `extrude001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||||
|
|> extrude(1, %)
|
||||||
|
`
|
||||||
|
const ast = parse(exampleCode)
|
||||||
|
if (err(ast)) throw ast
|
||||||
|
const extrudable = doesSceneHaveExtrudedSketch(ast)
|
||||||
|
expect(extrudable).toBeTruthy()
|
||||||
|
})
|
||||||
|
it('finds no extrusion with sketch only', async () => {
|
||||||
|
const exampleCode = `extrude001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||||
|
`
|
||||||
|
const ast = parse(exampleCode)
|
||||||
|
if (err(ast)) throw ast
|
||||||
|
const extrudable = doesSceneHaveExtrudedSketch(ast)
|
||||||
expect(extrudable).toBeFalsy()
|
expect(extrudable).toBeFalsy()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -666,8 +709,7 @@ myNestedVar = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
`
|
`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
let pathToNode: PathToNode = []
|
let pathToNode: PathToNode = []
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
enter: (node, path) => {
|
enter: (node, path) => {
|
||||||
@ -689,6 +731,7 @@ myNestedVar = [
|
|||||||
const pathToNode2 = getNodePathFromSourceRange(ast, [
|
const pathToNode2 = getNodePathFromSourceRange(ast, [
|
||||||
literalIndex + 2,
|
literalIndex + 2,
|
||||||
literalIndex + 2,
|
literalIndex + 2,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
expect(pathToNode).toEqual(pathToNode2)
|
expect(pathToNode).toEqual(pathToNode2)
|
||||||
})
|
})
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
sketchFromKclValueOptional,
|
sketchFromKclValueOptional,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
|
sourceRangeFromRust,
|
||||||
SyntaxType,
|
SyntaxType,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
@ -173,6 +174,30 @@ function moreNodePathFromSourceRange(
|
|||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_node.type === 'CallExpressionKw' && isInRange) {
|
||||||
|
const { callee, arguments: args } = _node
|
||||||
|
if (
|
||||||
|
callee.type === 'Identifier' &&
|
||||||
|
callee.start <= start &&
|
||||||
|
callee.end >= end
|
||||||
|
) {
|
||||||
|
path.push(['callee', 'CallExpressionKw'])
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
if (args.length > 0) {
|
||||||
|
for (let argIndex = 0; argIndex < args.length; argIndex++) {
|
||||||
|
const arg = args[argIndex].arg
|
||||||
|
if (arg.start <= start && arg.end >= end) {
|
||||||
|
path.push(['arguments', 'CallExpressionKw'])
|
||||||
|
path.push([argIndex, 'index'])
|
||||||
|
return moreNodePathFromSourceRange(arg, sourceRange, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
if (_node.type === 'BinaryExpression' && isInRange) {
|
if (_node.type === 'BinaryExpression' && isInRange) {
|
||||||
const { left, right } = _node
|
const { left, right } = _node
|
||||||
if (left.start <= start && left.end >= end) {
|
if (left.start <= start && left.end >= end) {
|
||||||
@ -234,13 +259,10 @@ function moreNodePathFromSourceRange(
|
|||||||
return moreNodePathFromSourceRange(expression, sourceRange, path)
|
return moreNodePathFromSourceRange(expression, sourceRange, path)
|
||||||
}
|
}
|
||||||
if (_node.type === 'VariableDeclaration' && isInRange) {
|
if (_node.type === 'VariableDeclaration' && isInRange) {
|
||||||
const declarations = _node.declarations
|
const declaration = _node.declaration
|
||||||
|
|
||||||
for (let decIndex = 0; decIndex < declarations.length; decIndex++) {
|
|
||||||
const declaration = declarations[decIndex]
|
|
||||||
if (declaration.start <= start && declaration.end >= end) {
|
if (declaration.start <= start && declaration.end >= end) {
|
||||||
path.push(['declarations', 'VariableDeclaration'])
|
path.push(['declaration', 'VariableDeclaration'])
|
||||||
path.push([decIndex, 'index'])
|
|
||||||
const init = declaration.init
|
const init = declaration.init
|
||||||
if (init.start <= start && init.end >= end) {
|
if (init.start <= start && init.end >= end) {
|
||||||
path.push(['init', ''])
|
path.push(['init', ''])
|
||||||
@ -248,22 +270,17 @@ function moreNodePathFromSourceRange(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (_node.type === 'VariableDeclaration' && isInRange) {
|
if (_node.type === 'VariableDeclaration' && isInRange) {
|
||||||
const declarations = _node.declarations
|
const declaration = _node.declaration
|
||||||
|
|
||||||
for (let decIndex = 0; decIndex < declarations.length; decIndex++) {
|
|
||||||
const declaration = declarations[decIndex]
|
|
||||||
if (declaration.start <= start && declaration.end >= end) {
|
if (declaration.start <= start && declaration.end >= end) {
|
||||||
const init = declaration.init
|
const init = declaration.init
|
||||||
if (init.start <= start && init.end >= end) {
|
if (init.start <= start && init.end >= end) {
|
||||||
path.push(['declarations', 'VariableDeclaration'])
|
path.push(['declaration', 'VariableDeclaration'])
|
||||||
path.push([decIndex, 'index'])
|
|
||||||
path.push(['init', ''])
|
path.push(['init', ''])
|
||||||
return moreNodePathFromSourceRange(init, sourceRange, path)
|
return moreNodePathFromSourceRange(init, sourceRange, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
if (_node.type === 'UnaryExpression' && isInRange) {
|
if (_node.type === 'UnaryExpression' && isInRange) {
|
||||||
@ -355,17 +372,23 @@ function moreNodePathFromSourceRange(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_node.type === 'ImportStatement' && isInRange) {
|
if (_node.type === 'ImportStatement' && isInRange) {
|
||||||
const { items } = _node
|
if (_node.selector && _node.selector.type === 'List') {
|
||||||
|
path.push(['selector', 'ImportStatement'])
|
||||||
|
const { items } = _node.selector
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
const item = items[i]
|
const item = items[i]
|
||||||
if (item.start <= start && item.end >= end) {
|
if (item.start <= start && item.end >= end) {
|
||||||
path.push(['items', 'ImportStatement'])
|
path.push(['items', 'ImportSelector'])
|
||||||
path.push([i, 'index'])
|
path.push([i, 'index'])
|
||||||
if (item.name.start <= start && item.name.end >= end) {
|
if (item.name.start <= start && item.name.end >= end) {
|
||||||
path.push(['name', 'ImportItem'])
|
path.push(['name', 'ImportItem'])
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
if (item.alias && item.alias.start <= start && item.alias.end >= end) {
|
if (
|
||||||
|
item.alias &&
|
||||||
|
item.alias.start <= start &&
|
||||||
|
item.alias.end >= end
|
||||||
|
) {
|
||||||
path.push(['alias', 'ImportItem'])
|
path.push(['alias', 'ImportItem'])
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
@ -374,6 +397,7 @@ function moreNodePathFromSourceRange(
|
|||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.error('not implemented: ' + node.type)
|
console.error('not implemented: ' + node.type)
|
||||||
|
|
||||||
@ -426,13 +450,10 @@ export function traverse(
|
|||||||
traverse(node, option, pathToNode)
|
traverse(node, option, pathToNode)
|
||||||
|
|
||||||
if (_node.type === 'VariableDeclaration') {
|
if (_node.type === 'VariableDeclaration') {
|
||||||
_node.declarations.forEach((declaration, index) =>
|
_traverse(_node.declaration, [
|
||||||
_traverse(declaration, [
|
|
||||||
...pathToNode,
|
...pathToNode,
|
||||||
['declarations', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
[index, 'index'],
|
|
||||||
])
|
])
|
||||||
)
|
|
||||||
} else if (_node.type === 'VariableDeclarator') {
|
} else if (_node.type === 'VariableDeclarator') {
|
||||||
_traverse(_node.init, [...pathToNode, ['init', '']])
|
_traverse(_node.init, [...pathToNode, ['init', '']])
|
||||||
} else if (_node.type === 'PipeExpression') {
|
} else if (_node.type === 'PipeExpression') {
|
||||||
@ -542,7 +563,7 @@ export function findAllPreviousVariablesPath(
|
|||||||
const variables: PrevVariable<any>[] = []
|
const variables: PrevVariable<any>[] = []
|
||||||
bodyItems?.forEach?.((item) => {
|
bodyItems?.forEach?.((item) => {
|
||||||
if (item.type !== 'VariableDeclaration' || item.end > startRange) return
|
if (item.type !== 'VariableDeclaration' || item.end > startRange) return
|
||||||
const varName = item.declarations[0].id.name
|
const varName = item.declaration.id.name
|
||||||
const varValue = programMemory?.get(varName)
|
const varValue = programMemory?.get(varName)
|
||||||
if (!varValue || typeof varValue?.value !== type) return
|
if (!varValue || typeof varValue?.value !== type) return
|
||||||
variables.push({
|
variables.push({
|
||||||
@ -645,7 +666,7 @@ export function isNodeSafeToReplacePath(
|
|||||||
|
|
||||||
export function isNodeSafeToReplace(
|
export function isNodeSafeToReplace(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
sourceRange: [number, number]
|
sourceRange: SourceRange
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
isSafe: boolean
|
isSafe: boolean
|
||||||
@ -736,7 +757,7 @@ export function isLinesParallelAndConstrained(
|
|||||||
const _varDec = getNodeFromPath(ast, primaryPath, 'VariableDeclaration')
|
const _varDec = getNodeFromPath(ast, primaryPath, 'VariableDeclaration')
|
||||||
if (err(_varDec)) return _varDec
|
if (err(_varDec)) return _varDec
|
||||||
const varDec = _varDec.node
|
const varDec = _varDec.node
|
||||||
const varName = (varDec as VariableDeclaration)?.declarations[0]?.id?.name
|
const varName = (varDec as VariableDeclaration)?.declaration.id?.name
|
||||||
const sg = sketchFromKclValue(programMemory?.get(varName), varName)
|
const sg = sketchFromKclValue(programMemory?.get(varName), varName)
|
||||||
if (err(sg)) return sg
|
if (err(sg)) return sg
|
||||||
const _primarySegment = getSketchSegmentFromSourceRange(
|
const _primarySegment = getSketchSegmentFromSourceRange(
|
||||||
@ -797,7 +818,7 @@ export function isLinesParallelAndConstrained(
|
|||||||
return {
|
return {
|
||||||
isParallelAndConstrained,
|
isParallelAndConstrained,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange(prevSourceRange, ast),
|
codeRef: codeRefFromRange(sourceRangeFromRust(prevSourceRange), ast),
|
||||||
artifact: artifactGraph.get(prevSegment.__geoMeta.id),
|
artifact: artifactGraph.get(prevSegment.__geoMeta.id),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -856,7 +877,7 @@ export function hasExtrudeSketch({
|
|||||||
}
|
}
|
||||||
const varDec = varDecMeta.node
|
const varDec = varDecMeta.node
|
||||||
if (varDec.type !== 'VariableDeclaration') return false
|
if (varDec.type !== 'VariableDeclaration') return false
|
||||||
const varName = varDec.declarations[0].id.name
|
const varName = varDec.declaration.id.name
|
||||||
const varValue = programMemory?.get(varName)
|
const varValue = programMemory?.get(varName)
|
||||||
return (
|
return (
|
||||||
varValue?.type === 'Solid' ||
|
varValue?.type === 'Solid' ||
|
||||||
@ -933,7 +954,8 @@ export function findUsesOfTagInPipe(
|
|||||||
return
|
return
|
||||||
const tagArgValue =
|
const tagArgValue =
|
||||||
tagArg.type === 'TagDeclarator' ? String(tagArg.value) : tagArg.name
|
tagArg.type === 'TagDeclarator' ? String(tagArg.value) : tagArg.name
|
||||||
if (tagArgValue === tag) dependentRanges.push([node.start, node.end])
|
if (tagArgValue === tag)
|
||||||
|
dependentRanges.push([node.start, node.end, true])
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return dependentRanges
|
return dependentRanges
|
||||||
@ -975,7 +997,9 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
if (
|
if (
|
||||||
node.type === 'CallExpression' &&
|
node.type === 'CallExpression' &&
|
||||||
node.callee.type === 'Identifier' &&
|
node.callee.type === 'Identifier' &&
|
||||||
(node.callee.name === 'extrude' || node.callee.name === 'revolve') &&
|
(node.callee.name === 'extrude' ||
|
||||||
|
node.callee.name === 'revolve' ||
|
||||||
|
node.callee.name === 'loft') &&
|
||||||
node.arguments?.[1]?.type === 'Identifier' &&
|
node.arguments?.[1]?.type === 'Identifier' &&
|
||||||
node.arguments[1].name === varDec.id.name
|
node.arguments[1].name === varDec.id.name
|
||||||
) {
|
) {
|
||||||
@ -988,7 +1012,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** File must contain at least one sketch that has not been extruded already */
|
/** File must contain at least one sketch that has not been extruded already */
|
||||||
export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
export function doesSceneHaveSweepableSketch(ast: Node<Program>, count = 1) {
|
||||||
const theMap: any = {}
|
const theMap: any = {}
|
||||||
traverse(ast as any, {
|
traverse(ast as any, {
|
||||||
enter(node) {
|
enter(node) {
|
||||||
@ -1037,6 +1061,35 @@ export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
return Object.keys(theMap).length >= count
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doesSceneHaveExtrudedSketch(ast: Node<Program>) {
|
||||||
|
const theMap: any = {}
|
||||||
|
traverse(ast as any, {
|
||||||
|
enter(node) {
|
||||||
|
if (
|
||||||
|
node.type === 'VariableDeclarator' &&
|
||||||
|
node.init?.type === 'PipeExpression'
|
||||||
|
) {
|
||||||
|
for (const pipe of node.init.body) {
|
||||||
|
if (
|
||||||
|
pipe.type === 'CallExpression' &&
|
||||||
|
pipe.callee.name === 'extrude'
|
||||||
|
) {
|
||||||
|
theMap[node.id.name] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
node.type === 'CallExpression' &&
|
||||||
|
node.callee.name === 'extrude' &&
|
||||||
|
node.arguments[1]?.type === 'Identifier'
|
||||||
|
) {
|
||||||
|
theMap[node.moduleId] = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
return Object.keys(theMap).length > 0
|
return Object.keys(theMap).length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse, Program, recast, initPromise } from './wasm'
|
import { assertParse, Program, recast, initPromise } from './wasm'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
@ -394,8 +394,6 @@ describe('it recasts binary expression using brackets where needed', () => {
|
|||||||
// helpers
|
// helpers
|
||||||
|
|
||||||
function code2ast(code: string): { ast: Program } {
|
function code2ast(code: string): { ast: Program } {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
// eslint-ignore-next-line
|
|
||||||
if (err(ast)) throw ast
|
|
||||||
return { ast }
|
return { ast }
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ Map {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
"range": [
|
"range": [
|
||||||
37,
|
12,
|
||||||
64,
|
31,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { makeDefaultPlanes, parse, initPromise, Program } from 'lang/wasm'
|
import { makeDefaultPlanes, assertParse, initPromise, Program } from 'lang/wasm'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import {
|
import {
|
||||||
OrderedCommand,
|
OrderedCommand,
|
||||||
@ -148,11 +148,7 @@ beforeAll(async () => {
|
|||||||
][]
|
][]
|
||||||
const cacheToWriteToFileTemp: Partial<CacheShape> = {}
|
const cacheToWriteToFileTemp: Partial<CacheShape> = {}
|
||||||
for (const [codeKey, code] of cacheEntries) {
|
for (const [codeKey, code] of cacheEntries) {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) {
|
|
||||||
console.error(ast)
|
|
||||||
return Promise.reject(ast)
|
|
||||||
}
|
|
||||||
await kclManager.executeAst({ ast })
|
await kclManager.executeAst({ ast })
|
||||||
|
|
||||||
cacheToWriteToFileTemp[codeKey] = {
|
cacheToWriteToFileTemp[codeKey] = {
|
||||||
@ -403,11 +399,7 @@ describe('capture graph of sketchOnFaceOnFace...', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function getCommands(codeKey: CodeKey): CacheShape[CodeKey] & { ast: Program } {
|
function getCommands(codeKey: CodeKey): CacheShape[CodeKey] & { ast: Program } {
|
||||||
const ast = parse(codeKey)
|
const ast = assertParse(codeKey)
|
||||||
if (err(ast)) {
|
|
||||||
console.error(ast)
|
|
||||||
throw ast
|
|
||||||
}
|
|
||||||
const file = fs.readFileSync(fullPath, 'utf-8')
|
const file = fs.readFileSync(fullPath, 'utf-8')
|
||||||
const parsed: CacheShape = JSON.parse(file)
|
const parsed: CacheShape = JSON.parse(file)
|
||||||
// these either already exist from the last run, or were created in
|
// these either already exist from the last run, or were created in
|
||||||
|
Before Width: | Height: | Size: 378 KiB After Width: | Height: | Size: 357 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 613 KiB After Width: | Height: | Size: 577 KiB |