Compare commits
15 Commits
v0.25.5
...
kurt-zoom-
Author | SHA1 | Date | |
---|---|---|---|
252fde9be6 | |||
cd91774881 | |||
88cd27425e | |||
a284a270b7 | |||
e2f5ad47a2 | |||
2a3693651a | |||
9ca49c6366 | |||
2a2e4a8b63 | |||
258bce8adc | |||
a3c0a2b03b | |||
2ed2e9cf86 | |||
438d1ec746 | |||
5112b48324 | |||
8cb17a8936 | |||
125207f60c |
68
.github/workflows/build-test-publish-apps.yml
vendored
@ -51,8 +51,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons
|
VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons
|
||||||
|
|
||||||
# TODO: see if we need to inject updater nightly URL here https://dl.zoo.dev/releases/modeling-app/nightly/last_update.json
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: prepared-files
|
name: prepared-files
|
||||||
@ -63,12 +61,25 @@ jobs:
|
|||||||
- id: export_version
|
- id: export_version
|
||||||
run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT"
|
run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Prepare electron-builder.yml file for nightly
|
||||||
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
|
run: |
|
||||||
|
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' electron-builder.yml
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
|
with:
|
||||||
|
name: prepared-files-nightly
|
||||||
|
path: |
|
||||||
|
electron-builder.yml
|
||||||
|
|
||||||
- name: Prepare electron-builder.yml file for updater test
|
- name: Prepare electron-builder.yml file for updater test
|
||||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||||
run: |
|
run: |
|
||||||
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/updater-test"' electron-builder.yml
|
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/updater-test"' electron-builder.yml
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||||
with:
|
with:
|
||||||
name: prepared-files-updater-test
|
name: prepared-files-updater-test
|
||||||
path: |
|
path: |
|
||||||
@ -108,6 +119,16 @@ jobs:
|
|||||||
mkdir src/wasm-lib/pkg
|
mkdir src/wasm-lib/pkg
|
||||||
cp prepared-files/src/wasm-lib/pkg/wasm_lib* src/wasm-lib/pkg
|
cp prepared-files/src/wasm-lib/pkg/wasm_lib* src/wasm-lib/pkg
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
|
name: prepared-files-nightly
|
||||||
|
|
||||||
|
- name: Copy updated electron-builder.yml file for nightly build
|
||||||
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
|
run: |
|
||||||
|
ls -R prepared-files-nightly
|
||||||
|
cp prepared-files-nightly/electron-builder.yml electron-builder.yml
|
||||||
|
|
||||||
- name: Sync node version and setup cache
|
- name: Sync node version and setup cache
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
@ -152,11 +173,17 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: out-${{ matrix.os }}
|
name: out-arm64-${{ matrix.os }}
|
||||||
path: |
|
path: |
|
||||||
out/Zoo*.*
|
out/Zoo*arm64*.*
|
||||||
out/latest*.yml
|
out/latest*.yml
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: out-x64-${{ matrix.os }}
|
||||||
|
path: |
|
||||||
|
out/Zoo*x*64*.*
|
||||||
|
|
||||||
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back
|
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
@ -176,10 +203,16 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||||
with:
|
with:
|
||||||
name: updater-test-${{ matrix.os }}
|
name: updater-test-arm64-${{ matrix.os }}
|
||||||
path: |
|
path: |
|
||||||
out/Zoo*.*
|
out/Zoo*arm64*.*
|
||||||
out/latest*.yml
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||||
|
with:
|
||||||
|
name: updater-test-x64-${{ matrix.os }}
|
||||||
|
path: |
|
||||||
|
out/Zoo*x64*.*
|
||||||
|
|
||||||
|
|
||||||
publish-apps-release:
|
publish-apps-release:
|
||||||
@ -201,17 +234,32 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: out-windows-2022
|
name: out-arm64-windows-2022
|
||||||
path: out
|
path: out
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: out-macos-14
|
name: out-x64-windows-2022
|
||||||
path: out
|
path: out
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: out-ubuntu-22.04
|
name: out-arm64-macos-14
|
||||||
|
path: out
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: out-x64-macos-14
|
||||||
|
path: out
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: out-arm64-ubuntu-22.04
|
||||||
|
path: out
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: out-x64-ubuntu-22.04
|
||||||
path: out
|
path: out
|
||||||
|
|
||||||
- name: Generate the download static endpoint
|
- name: Generate the download static endpoint
|
||||||
|
@ -19,7 +19,6 @@ layout: manual
|
|||||||
* [`angledLineToX`](kcl/angledLineToX)
|
* [`angledLineToX`](kcl/angledLineToX)
|
||||||
* [`angledLineToY`](kcl/angledLineToY)
|
* [`angledLineToY`](kcl/angledLineToY)
|
||||||
* [`arc`](kcl/arc)
|
* [`arc`](kcl/arc)
|
||||||
* [`arrayReduce`](kcl/arrayReduce)
|
|
||||||
* [`asin`](kcl/asin)
|
* [`asin`](kcl/asin)
|
||||||
* [`assert`](kcl/assert)
|
* [`assert`](kcl/assert)
|
||||||
* [`assertEqual`](kcl/assertEqual)
|
* [`assertEqual`](kcl/assertEqual)
|
||||||
@ -62,6 +61,7 @@ layout: manual
|
|||||||
* [`log10`](kcl/log10)
|
* [`log10`](kcl/log10)
|
||||||
* [`log2`](kcl/log2)
|
* [`log2`](kcl/log2)
|
||||||
* [`m`](kcl/m)
|
* [`m`](kcl/m)
|
||||||
|
* [`map`](kcl/map)
|
||||||
* [`max`](kcl/max)
|
* [`max`](kcl/max)
|
||||||
* [`min`](kcl/min)
|
* [`min`](kcl/min)
|
||||||
* [`mirror2d`](kcl/mirror2d)
|
* [`mirror2d`](kcl/mirror2d)
|
||||||
@ -78,6 +78,7 @@ layout: manual
|
|||||||
* [`profileStart`](kcl/profileStart)
|
* [`profileStart`](kcl/profileStart)
|
||||||
* [`profileStartX`](kcl/profileStartX)
|
* [`profileStartX`](kcl/profileStartX)
|
||||||
* [`profileStartY`](kcl/profileStartY)
|
* [`profileStartY`](kcl/profileStartY)
|
||||||
|
* [`reduce`](kcl/reduce)
|
||||||
* [`rem`](kcl/rem)
|
* [`rem`](kcl/rem)
|
||||||
* [`revolve`](kcl/revolve)
|
* [`revolve`](kcl/revolve)
|
||||||
* [`segAng`](kcl/segAng)
|
* [`segAng`](kcl/segAng)
|
||||||
|
56
docs/kcl/map.md
Normal file
47
docs/kcl/reduce.md
Normal file
18452
docs/kcl/std.json
89
docs/kcl/types/BinaryOperator.md
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
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:** `^`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
161
docs/kcl/types/BinaryPart.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
title: "BinaryPart"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Literal`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `BinaryExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `CallExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `optional` |`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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `UnaryExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `IfExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
75
docs/kcl/types/BodyItem.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
title: "BodyItem"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ExpressionStatement`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `VariableDeclaration`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ReturnStatement`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
docs/kcl/types/CommentStyle.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "CommentStyle"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Like // foo
|
||||||
|
|
||||||
|
**enum:** `line`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Like /* foo */
|
||||||
|
|
||||||
|
**enum:** `block`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/ElseIf.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ElseIf"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `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 |
|
||||||
|
|
||||||
|
|
21
docs/kcl/types/Environment.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: "Environment"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `bindings` |`object`| | No |
|
||||||
|
| `parent` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
|
||||||
|
|
16
docs/kcl/types/EnvironmentRef.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: "EnvironmentRef"
|
||||||
|
excerpt: "An index pointing to an environment."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
An index pointing to an environment.
|
||||||
|
|
||||||
|
**Type:** `integer` (`uint`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
297
docs/kcl/types/Expr.md
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
---
|
||||||
|
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 |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `BinaryExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `CallExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
| `optional` |`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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `PipeExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `PipeSubstitution`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ArrayExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ObjectExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `UnaryExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `IfExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
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` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/FunctionExpression.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "FunctionExpression"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `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 |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/Identifier.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "Identifier"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
195
docs/kcl/types/KclValue.md
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
---
|
||||||
|
title: "KclValue"
|
||||||
|
excerpt: "A memory item."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A memory item.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `UserVal`| | No |
|
||||||
|
| `value` |``| | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`TagIdentifier`](/docs/kcl/types#tag-identifier)| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `info` |[`TagEngineInfo`](/docs/kcl/types/TagEngineInfo)| | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A plane.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Plane`| | No |
|
||||||
|
| `id` |`string`| The id of the plane. | No |
|
||||||
|
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A memory item. | No |
|
||||||
|
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||||
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s X axis be? | No |
|
||||||
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s Y axis be? | No |
|
||||||
|
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A face.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Face`| | No |
|
||||||
|
| `id` |`string`| The id of the face. | No |
|
||||||
|
| `value` |`string`| The tag of the face. | No |
|
||||||
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s X axis be? | No |
|
||||||
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s Y axis be? | No |
|
||||||
|
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||||
|
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
An solid is a collection of extrude surfaces.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Solid`](/docs/kcl/types/Solid)| | No |
|
||||||
|
| `id` |`string`| The id of the solid. | No |
|
||||||
|
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
||||||
|
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
||||||
|
| `height` |`number`| The height of the solid. | No |
|
||||||
|
| `startCapId` |`string`| The id of the extrusion start cap | No |
|
||||||
|
| `endCapId` |`string`| The id of the extrusion end cap | No |
|
||||||
|
| `edgeCuts` |`[` [`EdgeCut`](/docs/kcl/types/EdgeCut) `]`| Chamfers or fillets on this solid. | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Solids`| | No |
|
||||||
|
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Data for an imported geometry.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`ImportedGeometry`](/docs/kcl/types/ImportedGeometry)| | No |
|
||||||
|
| `id` |`string`| The ID of the imported geometry. | No |
|
||||||
|
| `value` |`[` `string` `]`| The original file paths. | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Function`| | No |
|
||||||
|
| `expression` |[`FunctionExpression`](/docs/kcl/types/FunctionExpression)| A memory item. | No |
|
||||||
|
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| A memory item. | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
56
docs/kcl/types/LiteralIdentifier.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
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 |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Literal`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
57
docs/kcl/types/LiteralValue.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
title: "LiteralValue"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `integer` (`int64`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `number` (`double`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `string`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `boolean`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
57
docs/kcl/types/MemberObject.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
title: "MemberObject"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
22
docs/kcl/types/NonCodeMeta.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeMeta"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `nonCodeNodes` |`object`| | No |
|
||||||
|
| `start` |`[` [`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 |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/NonCodeNode.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeNode"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `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 |
|
||||||
|
|
||||||
|
|
103
docs/kcl/types/NonCodeValue.md
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeValue"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
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 |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `shebang`| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/ObjectProperty.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ObjectProperty"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `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 |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/Parameter.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
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 |
|
||||||
|
|
||||||
|
|
25
docs/kcl/types/Program.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
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 |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | 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 |
|
||||||
|
|
||||||
|
|
22
docs/kcl/types/ProgramMemory.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "ProgramMemory"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No |
|
||||||
|
| `currentEnv` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `return` |[`KclValue`](/docs/kcl/types/KclValue)| | No |
|
||||||
|
|
||||||
|
|
41
docs/kcl/types/UnaryOperator.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "UnaryOperator"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Negate a number.
|
||||||
|
|
||||||
|
**enum:** `-`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Negate a boolean.
|
||||||
|
|
||||||
|
**enum:** `!`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/VariableDeclarator.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "VariableDeclarator"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `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 |
|
||||||
|
|
||||||
|
|
65
docs/kcl/types/VariableKind.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
title: "VariableKind"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Declare a variable.
|
||||||
|
|
||||||
|
**enum:** `let`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Declare a variable that is read-only.
|
||||||
|
|
||||||
|
**enum:** `const`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Declare a function.
|
||||||
|
|
||||||
|
**enum:** `fn`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Declare a variable.
|
||||||
|
|
||||||
|
**enum:** `var`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -155,15 +155,16 @@ test(
|
|||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
|
||||||
|
|
||||||
// Expect it to succeed.
|
|
||||||
await expect(errorToastMessage).not.toBeVisible()
|
|
||||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
|
||||||
|
|
||||||
const successToastMessage = page.getByText(`Exported successfully`)
|
const successToastMessage = page.getByText(`Exported successfully`)
|
||||||
await expect(successToastMessage).toBeVisible()
|
await test.step('Check the success toast message shows and nothing else', async () =>
|
||||||
await expect(exportingToastMessage).not.toBeVisible()
|
Promise.all([
|
||||||
|
expect(alreadyExportingToastMessage).not.toBeVisible(),
|
||||||
|
expect(errorToastMessage).not.toBeVisible(),
|
||||||
|
expect(engineErrorToastMessage).not.toBeVisible(),
|
||||||
|
expect(successToastMessage).toBeVisible(),
|
||||||
|
expect(exportingToastMessage).not.toBeVisible(),
|
||||||
|
]))
|
||||||
|
|
||||||
await test.step('Check the export size', async () => {
|
await test.step('Check the export size', async () => {
|
||||||
await expect
|
await expect
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { _test, _expect } from './playwright-deprecated'
|
||||||
|
import { test, expect } from './fixtures/fixtureSetup'
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import {
|
import {
|
||||||
@ -11,14 +12,98 @@ import {
|
|||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { FILE_EXT } from 'lib/constants'
|
import { FILE_EXT } from 'lib/constants'
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
_test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
await setup(context, page, testInfo)
|
await setup(context, page, testInfo)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
_test.afterEach(async ({ page }, testInfo) => {
|
||||||
await tearDown(page, testInfo)
|
await tearDown(page, testInfo)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('integrations tests', () => {
|
||||||
|
test(
|
||||||
|
'Creating a new file or switching file while in sketchMode should exit sketchMode',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ tronApp, homePage, scene, editor, toolbar }) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'windows times out will waiting for the execution indicator?'
|
||||||
|
)
|
||||||
|
await tronApp.initialise({
|
||||||
|
fixtures: { homePage, scene, editor, toolbar },
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'test-sample')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const [clickObj] = await scene.makeMouseHelpers(600, 300)
|
||||||
|
|
||||||
|
await test.step('setup test', async () => {
|
||||||
|
await homePage.expectState({
|
||||||
|
projectCards: [
|
||||||
|
{
|
||||||
|
title: 'test-sample',
|
||||||
|
fileCount: 1,
|
||||||
|
folderCount: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sortBy: 'last-modified-desc',
|
||||||
|
})
|
||||||
|
await homePage.openProject('test-sample')
|
||||||
|
// windows times out here, hence the skip above
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
})
|
||||||
|
await test.step('enter sketch mode', async () => {
|
||||||
|
await clickObj()
|
||||||
|
await scene.moveNoWhere()
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: [
|
||||||
|
'|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]',
|
||||||
|
],
|
||||||
|
highlightedCode: '',
|
||||||
|
diagnostics: [],
|
||||||
|
})
|
||||||
|
await toolbar.editSketch()
|
||||||
|
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||||
|
})
|
||||||
|
await test.step('check sketch mode is exited when creating new file', async () => {
|
||||||
|
await toolbar.fileTreeBtn.click()
|
||||||
|
await toolbar.expectFileTreeState(['main.kcl'])
|
||||||
|
await toolbar.createFile({ wait: true })
|
||||||
|
|
||||||
|
// check we're out of sketch mode
|
||||||
|
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
||||||
|
await expect(toolbar.startSketchBtn).toBeVisible()
|
||||||
|
})
|
||||||
|
await test.step('setup for next assertion', async () => {
|
||||||
|
await toolbar.openFile('main.kcl')
|
||||||
|
await clickObj()
|
||||||
|
await scene.moveNoWhere()
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: [
|
||||||
|
'|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]',
|
||||||
|
],
|
||||||
|
highlightedCode: '',
|
||||||
|
diagnostics: [],
|
||||||
|
})
|
||||||
|
await toolbar.editSketch()
|
||||||
|
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||||
|
await toolbar.expectFileTreeState(['main.kcl', 'Untitled.kcl'])
|
||||||
|
})
|
||||||
|
await test.step('check sketch mode is exited when opening a different file', async () => {
|
||||||
|
await toolbar.openFile('untitled.kcl', { wait: false })
|
||||||
|
|
||||||
|
// check we're out of sketch mode
|
||||||
|
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
||||||
|
await expect(toolbar.startSketchBtn).toBeVisible()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
test.describe('when using the file tree to', () => {
|
test.describe('when using the file tree to', () => {
|
||||||
const fromFile = 'main.kcl'
|
const fromFile = 'main.kcl'
|
||||||
const toFile = 'hello.kcl'
|
const toFile = 'hello.kcl'
|
||||||
@ -26,11 +111,8 @@ test.describe('when using the file tree to', () => {
|
|||||||
test(
|
test(
|
||||||
`rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
|
`rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _, tronApp }, testInfo) => {
|
||||||
const { electronApp, page } = await setupElectron({
|
await tronApp.initialise()
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
panesOpen,
|
panesOpen,
|
||||||
@ -38,10 +120,10 @@ test.describe('when using the file tree to', () => {
|
|||||||
pasteCodeInEditor,
|
pasteCodeInEditor,
|
||||||
renameFile,
|
renameFile,
|
||||||
editorTextMatches,
|
editorTextMatches,
|
||||||
} = await getUtils(page, test)
|
} = await getUtils(tronApp.page, test)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||||
page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
await panesOpen(['files', 'code'])
|
||||||
|
|
||||||
@ -55,39 +137,38 @@ test.describe('when using the file tree to', () => {
|
|||||||
await pasteCodeInEditor(kclCube)
|
await pasteCodeInEditor(kclCube)
|
||||||
|
|
||||||
await renameFile(fromFile, toFile)
|
await renameFile(fromFile, toFile)
|
||||||
await page.reload()
|
await tronApp.page.reload()
|
||||||
|
|
||||||
await test.step('Postcondition: editor has same content as before the rename', async () => {
|
await test.step('Postcondition: editor has same content as before the rename', async () => {
|
||||||
await editorTextMatches(kclCube)
|
await editorTextMatches(kclCube)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Postcondition: opening and closing settings works', async () => {
|
await test.step('Postcondition: opening and closing settings works', async () => {
|
||||||
const settingsOpenButton = page.getByRole('link', {
|
const settingsOpenButton = tronApp.page.getByRole('link', {
|
||||||
name: 'settings Settings',
|
name: 'settings Settings',
|
||||||
})
|
})
|
||||||
const settingsCloseButton = page.getByTestId('settings-close-button')
|
const settingsCloseButton = tronApp.page.getByTestId(
|
||||||
|
'settings-close-button'
|
||||||
|
)
|
||||||
await settingsOpenButton.click()
|
await settingsOpenButton.click()
|
||||||
await settingsCloseButton.click()
|
await settingsCloseButton.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await tronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
`create many new untitled files they increment their names`,
|
`create many new untitled files they increment their names`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _, tronApp }, testInfo) => {
|
||||||
const { electronApp, page } = await setupElectron({
|
await tronApp.initialise()
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
const { panesOpen, createAndSelectProject, createNewFile } =
|
const { panesOpen, createAndSelectProject, createNewFile } =
|
||||||
await getUtils(page, test)
|
await getUtils(tronApp.page, test)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||||
page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files'])
|
await panesOpen(['files'])
|
||||||
|
|
||||||
@ -101,23 +182,21 @@ test.describe('when using the file tree to', () => {
|
|||||||
|
|
||||||
await test.step('Postcondition: there are 5 new Untitled-*.kcl files', async () => {
|
await test.step('Postcondition: there are 5 new Untitled-*.kcl files', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
page
|
tronApp.page
|
||||||
.locator('[data-testid="file-pane-scroll-container"] button')
|
.locator('[data-testid="file-pane-scroll-container"] button')
|
||||||
.filter({ hasText: /Untitled[-]?[0-5]?/ })
|
.filter({ hasText: /Untitled[-]?[0-5]?/ })
|
||||||
).toHaveCount(5)
|
).toHaveCount(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await tronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'create a new file with the same name as an existing file cancels the operation',
|
'create a new file with the same name as an existing file cancels the operation',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _, tronApp }, testInfo) => {
|
||||||
const { electronApp, page } = await setupElectron({
|
await tronApp.initialise()
|
||||||
testInfo,
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
openKclCodePanel,
|
openKclCodePanel,
|
||||||
@ -128,10 +207,10 @@ test.describe('when using the file tree to', () => {
|
|||||||
renameFile,
|
renameFile,
|
||||||
selectFile,
|
selectFile,
|
||||||
editorTextMatches,
|
editorTextMatches,
|
||||||
} = await getUtils(page, test)
|
} = await getUtils(tronApp.page, _test)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||||
page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
await createAndSelectProject('project-000')
|
||||||
await openKclCodePanel()
|
await openKclCodePanel()
|
||||||
@ -159,25 +238,22 @@ test.describe('when using the file tree to', () => {
|
|||||||
await selectFile(kcl1)
|
await selectFile(kcl1)
|
||||||
await editorTextMatches(kclCube)
|
await editorTextMatches(kclCube)
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(500)
|
await tronApp.page.waitForTimeout(500)
|
||||||
|
|
||||||
await test.step(`Postcondition: ${kcl2} still exists with the original content`, async () => {
|
await test.step(`Postcondition: ${kcl2} still exists with the original content`, async () => {
|
||||||
await selectFile(kcl2)
|
await selectFile(kcl2)
|
||||||
await editorTextMatches(kclCylinder)
|
await editorTextMatches(kclCylinder)
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await tronApp?.close?.()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'deleting all files recreates a default main.kcl with no code',
|
'deleting all files recreates a default main.kcl with no code',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _, tronApp }, testInfo) => {
|
||||||
const { electronApp, page } = await setupElectron({
|
await tronApp.initialise()
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
panesOpen,
|
panesOpen,
|
||||||
@ -185,10 +261,10 @@ test.describe('when using the file tree to', () => {
|
|||||||
pasteCodeInEditor,
|
pasteCodeInEditor,
|
||||||
deleteFile,
|
deleteFile,
|
||||||
editorTextMatches,
|
editorTextMatches,
|
||||||
} = await getUtils(page, test)
|
} = await getUtils(tronApp.page, _test)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||||
page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
await panesOpen(['files', 'code'])
|
||||||
|
|
||||||
@ -208,7 +284,7 @@ test.describe('when using the file tree to', () => {
|
|||||||
await editorTextMatches('')
|
await editorTextMatches('')
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await tronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -217,10 +293,8 @@ test.describe('when using the file tree to', () => {
|
|||||||
{
|
{
|
||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
},
|
},
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _, tronApp }, testInfo) => {
|
||||||
const { page } = await setupElectron({
|
await tronApp.initialise()
|
||||||
testInfo,
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
panesOpen,
|
panesOpen,
|
||||||
@ -230,10 +304,10 @@ test.describe('when using the file tree to', () => {
|
|||||||
openDebugPanel,
|
openDebugPanel,
|
||||||
closeDebugPanel,
|
closeDebugPanel,
|
||||||
expectCmdLog,
|
expectCmdLog,
|
||||||
} = await getUtils(page, test)
|
} = await getUtils(tronApp.page, test)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||||
page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
await panesOpen(['files', 'code'])
|
||||||
await createAndSelectProject('project-000')
|
await createAndSelectProject('project-000')
|
||||||
@ -248,30 +322,30 @@ test.describe('when using the file tree to', () => {
|
|||||||
|
|
||||||
// Create a large lego file
|
// Create a large lego file
|
||||||
await createNewFile('lego')
|
await createNewFile('lego')
|
||||||
const legoFile = page.getByRole('listitem').filter({
|
const legoFile = tronApp.page.getByRole('listitem').filter({
|
||||||
has: page.getByRole('button', { name: 'lego.kcl' }),
|
has: tronApp.page.getByRole('button', { name: 'lego.kcl' }),
|
||||||
})
|
})
|
||||||
await expect(legoFile).toBeVisible({ timeout: 60_000 })
|
await _expect(legoFile).toBeVisible({ timeout: 60_000 })
|
||||||
await legoFile.click()
|
await legoFile.click()
|
||||||
const kclLego = await fsp.readFile(
|
const kclLego = await fsp.readFile(
|
||||||
'src/wasm-lib/tests/executor/inputs/lego.kcl',
|
'src/wasm-lib/tests/executor/inputs/lego.kcl',
|
||||||
'utf-8'
|
'utf-8'
|
||||||
)
|
)
|
||||||
await pasteCodeInEditor(kclLego)
|
await pasteCodeInEditor(kclLego)
|
||||||
const mainFile = page.getByRole('listitem').filter({
|
const mainFile = tronApp.page.getByRole('listitem').filter({
|
||||||
has: page.getByRole('button', { name: 'main.kcl' }),
|
has: tronApp.page.getByRole('button', { name: 'main.kcl' }),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Open settings and enable the debug panel
|
// Open settings and enable the debug panel
|
||||||
await page
|
await tronApp.page
|
||||||
.getByRole('link', {
|
.getByRole('link', {
|
||||||
name: 'settings Settings',
|
name: 'settings Settings',
|
||||||
})
|
})
|
||||||
.click()
|
.click()
|
||||||
await page.locator('#showDebugPanel').getByText('OffOn').click()
|
await tronApp.page.locator('#showDebugPanel').getByText('OffOn').click()
|
||||||
await page.getByTestId('settings-close-button').click()
|
await tronApp.page.getByTestId('settings-close-button').click()
|
||||||
|
|
||||||
await test.step('swap between small and large files', async () => {
|
await _test.step('swap between small and large files', async () => {
|
||||||
await openDebugPanel()
|
await openDebugPanel()
|
||||||
// Previously created a file so we need to start back at main.kcl
|
// Previously created a file so we need to start back at main.kcl
|
||||||
await mainFile.click()
|
await mainFile.click()
|
||||||
@ -283,12 +357,14 @@ test.describe('when using the file tree to', () => {
|
|||||||
await expectCmdLog('[data-message-type="execution-done"]', 60_000)
|
await expectCmdLog('[data-message-type="execution-done"]', 60_000)
|
||||||
await closeDebugPanel()
|
await closeDebugPanel()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await tronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Renaming in the file tree', () => {
|
_test.describe('Renaming in the file tree', () => {
|
||||||
test(
|
_test(
|
||||||
'A file you have open',
|
'A file you have open',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _ }, testInfo) => {
|
||||||
@ -333,56 +409,56 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
const renameInput = page.getByPlaceholder('fileToRename.kcl')
|
const renameInput = page.getByPlaceholder('fileToRename.kcl')
|
||||||
const codeLocator = page.locator('.cm-content')
|
const codeLocator = page.locator('.cm-content')
|
||||||
|
|
||||||
await test.step('Open project and file pane', async () => {
|
await _test.step('Open project and file pane', async () => {
|
||||||
await expect(projectLink).toBeVisible()
|
await _expect(projectLink).toBeVisible()
|
||||||
await projectLink.click()
|
await projectLink.click()
|
||||||
await expect(projectMenuButton).toBeVisible()
|
await _expect(projectMenuButton).toBeVisible()
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
await expect(fileToRename).toBeVisible()
|
await _expect(fileToRename).toBeVisible()
|
||||||
expect(checkUnRenamedFS()).toBeTruthy()
|
_expect(checkUnRenamedFS()).toBeTruthy()
|
||||||
expect(checkRenamedFS()).toBeFalsy()
|
_expect(checkRenamedFS()).toBeFalsy()
|
||||||
await fileToRename.click()
|
await fileToRename.click()
|
||||||
await expect(projectMenuButton).toContainText('fileToRename.kcl')
|
await _expect(projectMenuButton).toContainText('fileToRename.kcl')
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(codeLocator).toContainText('circle(')
|
await _expect(codeLocator).toContainText('circle(')
|
||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Rename the file', async () => {
|
await _test.step('Rename the file', async () => {
|
||||||
await fileToRename.click({ button: 'right' })
|
await fileToRename.click({ button: 'right' })
|
||||||
await renameMenuItem.click()
|
await renameMenuItem.click()
|
||||||
await expect(renameInput).toBeVisible()
|
await _expect(renameInput).toBeVisible()
|
||||||
await renameInput.fill(newFileName)
|
await renameInput.fill(newFileName)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Verify the file is renamed', async () => {
|
await _test.step('Verify the file is renamed', async () => {
|
||||||
await expect(fileToRename).not.toBeAttached()
|
await _expect(fileToRename).not.toBeAttached()
|
||||||
await expect(renamedFile).toBeVisible()
|
await _expect(renamedFile).toBeVisible()
|
||||||
expect(checkUnRenamedFS()).toBeFalsy()
|
_expect(checkUnRenamedFS()).toBeFalsy()
|
||||||
expect(checkRenamedFS()).toBeTruthy()
|
_expect(checkRenamedFS()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Verify we navigated', async () => {
|
await _test.step('Verify we navigated', async () => {
|
||||||
await expect(projectMenuButton).toContainText(newFileName + FILE_EXT)
|
await _expect(projectMenuButton).toContainText(newFileName + FILE_EXT)
|
||||||
const url = page.url()
|
const url = page.url()
|
||||||
expect(url).toContain(newFileName)
|
_expect(url).toContain(newFileName)
|
||||||
await expect(projectMenuButton).not.toContainText('fileToRename.kcl')
|
await _expect(projectMenuButton).not.toContainText('fileToRename.kcl')
|
||||||
await expect(projectMenuButton).not.toContainText('main.kcl')
|
await _expect(projectMenuButton).not.toContainText('main.kcl')
|
||||||
expect(url).not.toContain('fileToRename.kcl')
|
_expect(url).not.toContain('fileToRename.kcl')
|
||||||
expect(url).not.toContain('main.kcl')
|
_expect(url).not.toContain('main.kcl')
|
||||||
|
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(codeLocator).toContainText('circle(')
|
await _expect(codeLocator).toContainText('circle(')
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
_test(
|
||||||
'A file you do not have open',
|
'A file you do not have open',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _ }, testInfo) => {
|
||||||
@ -426,54 +502,54 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
const renameInput = page.getByPlaceholder('fileToRename.kcl')
|
const renameInput = page.getByPlaceholder('fileToRename.kcl')
|
||||||
const codeLocator = page.locator('.cm-content')
|
const codeLocator = page.locator('.cm-content')
|
||||||
|
|
||||||
await test.step('Open project and file pane', async () => {
|
await _test.step('Open project and file pane', async () => {
|
||||||
await expect(projectLink).toBeVisible()
|
await _expect(projectLink).toBeVisible()
|
||||||
await projectLink.click()
|
await projectLink.click()
|
||||||
await expect(projectMenuButton).toBeVisible()
|
await _expect(projectMenuButton).toBeVisible()
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
await expect(fileToRename).toBeVisible()
|
await _expect(fileToRename).toBeVisible()
|
||||||
expect(checkUnRenamedFS()).toBeTruthy()
|
_expect(checkUnRenamedFS()).toBeTruthy()
|
||||||
expect(checkRenamedFS()).toBeFalsy()
|
_expect(checkRenamedFS()).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Rename the file', async () => {
|
await _test.step('Rename the file', async () => {
|
||||||
await fileToRename.click({ button: 'right' })
|
await fileToRename.click({ button: 'right' })
|
||||||
await renameMenuItem.click()
|
await renameMenuItem.click()
|
||||||
await expect(renameInput).toBeVisible()
|
await _expect(renameInput).toBeVisible()
|
||||||
await renameInput.fill(newFileName)
|
await renameInput.fill(newFileName)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Verify the file is renamed', async () => {
|
await _test.step('Verify the file is renamed', async () => {
|
||||||
await expect(fileToRename).not.toBeAttached()
|
await _expect(fileToRename).not.toBeAttached()
|
||||||
await expect(renamedFile).toBeVisible()
|
await _expect(renamedFile).toBeVisible()
|
||||||
expect(checkUnRenamedFS()).toBeFalsy()
|
_expect(checkUnRenamedFS()).toBeFalsy()
|
||||||
expect(checkRenamedFS()).toBeTruthy()
|
_expect(checkRenamedFS()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Verify we have not navigated', async () => {
|
await _test.step('Verify we have not navigated', async () => {
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
await expect(projectMenuButton).not.toContainText(
|
await _expect(projectMenuButton).not.toContainText(
|
||||||
newFileName + FILE_EXT
|
newFileName + FILE_EXT
|
||||||
)
|
)
|
||||||
await expect(projectMenuButton).not.toContainText('fileToRename.kcl')
|
await _expect(projectMenuButton).not.toContainText('fileToRename.kcl')
|
||||||
|
|
||||||
const url = page.url()
|
const url = page.url()
|
||||||
expect(url).toContain('main.kcl')
|
_expect(url).toContain('main.kcl')
|
||||||
expect(url).not.toContain(newFileName)
|
_expect(url).not.toContain(newFileName)
|
||||||
expect(url).not.toContain('fileToRename.kcl')
|
_expect(url).not.toContain('fileToRename.kcl')
|
||||||
|
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(codeLocator).toContainText('fillet(')
|
await _expect(codeLocator).toContainText('fillet(')
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
_test(
|
||||||
`A folder you're not inside`,
|
`A folder you're not inside`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _ }, testInfo) => {
|
||||||
@ -519,48 +595,51 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
return fs.existsSync(folderPath)
|
return fs.existsSync(folderPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
await test.step('Open project and file pane', async () => {
|
await _test.step('Open project and file pane', async () => {
|
||||||
await expect(projectLink).toBeVisible()
|
await _expect(projectLink).toBeVisible()
|
||||||
await projectLink.click()
|
await projectLink.click()
|
||||||
await expect(projectMenuButton).toBeVisible()
|
await _expect(projectMenuButton).toBeVisible()
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
|
||||||
const url = page.url()
|
const url = page.url()
|
||||||
expect(url).toContain('main.kcl')
|
_expect(url).toContain('main.kcl')
|
||||||
expect(url).not.toContain('folderToRename')
|
_expect(url).not.toContain('folderToRename')
|
||||||
|
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
await expect(folderToRename).toBeVisible()
|
await _expect(folderToRename).toBeVisible()
|
||||||
expect(checkUnRenamedFolderFS()).toBeTruthy()
|
_expect(checkUnRenamedFolderFS()).toBeTruthy()
|
||||||
expect(checkRenamedFolderFS()).toBeFalsy()
|
_expect(checkRenamedFolderFS()).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Rename the folder', async () => {
|
await _test.step('Rename the folder', async () => {
|
||||||
await folderToRename.click({ button: 'right' })
|
await folderToRename.click({ button: 'right' })
|
||||||
await expect(renameMenuItem).toBeVisible()
|
await _expect(renameMenuItem).toBeVisible()
|
||||||
await renameMenuItem.click()
|
await renameMenuItem.click()
|
||||||
await expect(renameInput).toBeVisible()
|
await _expect(renameInput).toBeVisible()
|
||||||
await renameInput.fill(newFolderName)
|
await renameInput.fill(newFolderName)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Verify the folder is renamed, and no navigation occurred', async () => {
|
await _test.step(
|
||||||
const url = page.url()
|
'Verify the folder is renamed, and no navigation occurred',
|
||||||
expect(url).toContain('main.kcl')
|
async () => {
|
||||||
expect(url).not.toContain('folderToRename')
|
const url = page.url()
|
||||||
|
_expect(url).toContain('main.kcl')
|
||||||
|
_expect(url).not.toContain('folderToRename')
|
||||||
|
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
await expect(renamedFolder).toBeVisible()
|
await _expect(renamedFolder).toBeVisible()
|
||||||
await expect(folderToRename).not.toBeAttached()
|
await _expect(folderToRename).not.toBeAttached()
|
||||||
expect(checkUnRenamedFolderFS()).toBeFalsy()
|
_expect(checkUnRenamedFolderFS()).toBeFalsy()
|
||||||
expect(checkRenamedFolderFS()).toBeTruthy()
|
_expect(checkRenamedFolderFS()).toBeTruthy()
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
_test(
|
||||||
`A folder you are inside`,
|
`A folder you are inside`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _ }, testInfo) => {
|
||||||
@ -609,66 +688,69 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
return fs.existsSync(folderPath)
|
return fs.existsSync(folderPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
await test.step('Open project and navigate into folder', async () => {
|
await _test.step('Open project and navigate into folder', async () => {
|
||||||
await expect(projectLink).toBeVisible()
|
await _expect(projectLink).toBeVisible()
|
||||||
await projectLink.click()
|
await projectLink.click()
|
||||||
await expect(projectMenuButton).toBeVisible()
|
await _expect(projectMenuButton).toBeVisible()
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
|
||||||
const url = page.url()
|
const url = page.url()
|
||||||
expect(url).toContain('main.kcl')
|
_expect(url).toContain('main.kcl')
|
||||||
expect(url).not.toContain('folderToRename')
|
_expect(url).not.toContain('folderToRename')
|
||||||
|
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
await expect(folderToRename).toBeVisible()
|
await _expect(folderToRename).toBeVisible()
|
||||||
await folderToRename.click()
|
await folderToRename.click()
|
||||||
await expect(fileWithinFolder).toBeVisible()
|
await _expect(fileWithinFolder).toBeVisible()
|
||||||
await fileWithinFolder.click()
|
await fileWithinFolder.click()
|
||||||
|
|
||||||
await expect(projectMenuButton).toContainText('someFileWithin.kcl')
|
await _expect(projectMenuButton).toContainText('someFileWithin.kcl')
|
||||||
const newUrl = page.url()
|
const newUrl = page.url()
|
||||||
expect(newUrl).toContain('folderToRename')
|
_expect(newUrl).toContain('folderToRename')
|
||||||
expect(newUrl).toContain('someFileWithin.kcl')
|
_expect(newUrl).toContain('someFileWithin.kcl')
|
||||||
expect(newUrl).not.toContain('main.kcl')
|
_expect(newUrl).not.toContain('main.kcl')
|
||||||
expect(checkUnRenamedFolderFS()).toBeTruthy()
|
_expect(checkUnRenamedFolderFS()).toBeTruthy()
|
||||||
expect(checkRenamedFolderFS()).toBeFalsy()
|
_expect(checkRenamedFolderFS()).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Rename the folder', async () => {
|
await _test.step('Rename the folder', async () => {
|
||||||
await page.waitForTimeout(60000)
|
await page.waitForTimeout(60000)
|
||||||
await folderToRename.click({ button: 'right' })
|
await folderToRename.click({ button: 'right' })
|
||||||
await expect(renameMenuItem).toBeVisible()
|
await _expect(renameMenuItem).toBeVisible()
|
||||||
await renameMenuItem.click()
|
await renameMenuItem.click()
|
||||||
await expect(renameInput).toBeVisible()
|
await _expect(renameInput).toBeVisible()
|
||||||
await renameInput.fill(newFolderName)
|
await renameInput.fill(newFolderName)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Verify the folder is renamed, and navigated to new path', async () => {
|
await _test.step(
|
||||||
const urlSnippet = encodeURIComponent(
|
'Verify the folder is renamed, and navigated to new path',
|
||||||
join(newFolderName, 'someFileWithin.kcl')
|
async () => {
|
||||||
)
|
const urlSnippet = encodeURIComponent(
|
||||||
await page.waitForURL(new RegExp(urlSnippet))
|
join(newFolderName, 'someFileWithin.kcl')
|
||||||
await expect(projectMenuButton).toContainText('someFileWithin.kcl')
|
)
|
||||||
await expect(renamedFolder).toBeVisible()
|
await page.waitForURL(new RegExp(urlSnippet))
|
||||||
await expect(folderToRename).not.toBeAttached()
|
await _expect(projectMenuButton).toContainText('someFileWithin.kcl')
|
||||||
|
await _expect(renamedFolder).toBeVisible()
|
||||||
|
await _expect(folderToRename).not.toBeAttached()
|
||||||
|
|
||||||
// URL is synchronous, so we check the other stuff first
|
// URL is synchronous, so we check the other stuff first
|
||||||
const url = page.url()
|
const url = page.url()
|
||||||
expect(url).not.toContain('main.kcl')
|
_expect(url).not.toContain('main.kcl')
|
||||||
expect(url).toContain(newFolderName)
|
_expect(url).toContain(newFolderName)
|
||||||
expect(url).toContain('someFileWithin.kcl')
|
_expect(url).toContain('someFileWithin.kcl')
|
||||||
expect(checkUnRenamedFolderFS()).toBeFalsy()
|
_expect(checkUnRenamedFolderFS()).toBeFalsy()
|
||||||
expect(checkRenamedFolderFS()).toBeTruthy()
|
_expect(checkRenamedFolderFS()).toBeTruthy()
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Deleting items from the file pane', () => {
|
_test.describe('Deleting items from the file pane', () => {
|
||||||
test(
|
_test(
|
||||||
`delete file when main.kcl exists, navigate to main.kcl`,
|
`delete file when main.kcl exists, navigate to main.kcl`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
@ -700,45 +782,48 @@ test.describe('Deleting items from the file pane', () => {
|
|||||||
const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
|
const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
|
||||||
const deleteConfirmation = page.getByTestId('delete-confirmation')
|
const deleteConfirmation = page.getByTestId('delete-confirmation')
|
||||||
|
|
||||||
await test.step('Open project and navigate to fileToDelete.kcl', async () => {
|
await _test.step(
|
||||||
await projectCard.click()
|
'Open project and navigate to fileToDelete.kcl',
|
||||||
await u.waitForPageLoad()
|
async () => {
|
||||||
await u.openFilePanel()
|
await projectCard.click()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
await u.openFilePanel()
|
||||||
|
|
||||||
await fileToDelete.click()
|
await fileToDelete.click()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(u.codeLocator).toContainText('getOppositeEdge(thing)')
|
await _expect(u.codeLocator).toContainText('getOppositeEdge(thing)')
|
||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
await test.step('Delete fileToDelete.kcl', async () => {
|
await _test.step('Delete fileToDelete.kcl', async () => {
|
||||||
await fileToDelete.click({ button: 'right' })
|
await fileToDelete.click({ button: 'right' })
|
||||||
await expect(deleteMenuItem).toBeVisible()
|
await _expect(deleteMenuItem).toBeVisible()
|
||||||
await deleteMenuItem.click()
|
await deleteMenuItem.click()
|
||||||
await expect(deleteConfirmation).toBeVisible()
|
await _expect(deleteConfirmation).toBeVisible()
|
||||||
await deleteConfirmation.click()
|
await deleteConfirmation.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Check deletion and navigation', async () => {
|
await _test.step('Check deletion and navigation', async () => {
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
await expect(fileToDelete).not.toBeVisible()
|
await _expect(fileToDelete).not.toBeVisible()
|
||||||
await u.closeFilePanel()
|
await u.closeFilePanel()
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(u.codeLocator).toContainText('circle(')
|
await _expect(u.codeLocator).toContainText('circle(')
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test.fixme(
|
_test.fixme(
|
||||||
'TODO - delete file we have open when main.kcl does not exist',
|
'TODO - delete file we have open when main.kcl does not exist',
|
||||||
async () => {}
|
async () => {}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
_test(
|
||||||
`Delete folder we are not in, don't navigate`,
|
`Delete folder we are not in, don't navigate`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
@ -772,32 +857,32 @@ test.describe('Deleting items from the file pane', () => {
|
|||||||
const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
|
const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
|
||||||
const deleteConfirmation = page.getByTestId('delete-confirmation')
|
const deleteConfirmation = page.getByTestId('delete-confirmation')
|
||||||
|
|
||||||
await test.step('Open project and open project pane', async () => {
|
await _test.step('Open project and open project pane', async () => {
|
||||||
await projectCard.click()
|
await projectCard.click()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Delete folderToDelete', async () => {
|
await _test.step('Delete folderToDelete', async () => {
|
||||||
await folderToDelete.click({ button: 'right' })
|
await folderToDelete.click({ button: 'right' })
|
||||||
await expect(deleteMenuItem).toBeVisible()
|
await _expect(deleteMenuItem).toBeVisible()
|
||||||
await deleteMenuItem.click()
|
await deleteMenuItem.click()
|
||||||
await expect(deleteConfirmation).toBeVisible()
|
await _expect(deleteConfirmation).toBeVisible()
|
||||||
await deleteConfirmation.click()
|
await deleteConfirmation.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Check deletion and no navigation', async () => {
|
await _test.step('Check deletion and no navigation', async () => {
|
||||||
await expect(folderToDelete).not.toBeAttached()
|
await _expect(folderToDelete).not.toBeAttached()
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
_test(
|
||||||
`Delete folder we are in, navigate to main.kcl`,
|
`Delete folder we are in, navigate to main.kcl`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
@ -834,36 +919,45 @@ test.describe('Deleting items from the file pane', () => {
|
|||||||
const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
|
const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
|
||||||
const deleteConfirmation = page.getByTestId('delete-confirmation')
|
const deleteConfirmation = page.getByTestId('delete-confirmation')
|
||||||
|
|
||||||
await test.step('Open project and navigate into folderToDelete', async () => {
|
await _test.step(
|
||||||
await projectCard.click()
|
'Open project and navigate into folderToDelete',
|
||||||
await u.waitForPageLoad()
|
async () => {
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await projectCard.click()
|
||||||
await u.closeKclCodePanel()
|
await u.waitForPageLoad()
|
||||||
await u.openFilePanel()
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
await u.closeKclCodePanel()
|
||||||
|
await u.openFilePanel()
|
||||||
|
|
||||||
await folderToDelete.click()
|
await folderToDelete.click()
|
||||||
await expect(fileWithinFolder).toBeVisible()
|
await _expect(fileWithinFolder).toBeVisible()
|
||||||
await fileWithinFolder.click()
|
await fileWithinFolder.click()
|
||||||
await expect(projectMenuButton).toContainText('someFileWithin.kcl')
|
await _expect(projectMenuButton).toContainText('someFileWithin.kcl')
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
await test.step('Delete folderToDelete', async () => {
|
await _test.step('Delete folderToDelete', async () => {
|
||||||
await folderToDelete.click({ button: 'right' })
|
await folderToDelete.click({ button: 'right' })
|
||||||
await expect(deleteMenuItem).toBeVisible()
|
await _expect(deleteMenuItem).toBeVisible()
|
||||||
await deleteMenuItem.click()
|
await deleteMenuItem.click()
|
||||||
await expect(deleteConfirmation).toBeVisible()
|
await _expect(deleteConfirmation).toBeVisible()
|
||||||
await deleteConfirmation.click()
|
await deleteConfirmation.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Check deletion and navigation to main.kcl', async () => {
|
await _test.step(
|
||||||
await expect(folderToDelete).not.toBeAttached()
|
'Check deletion and navigation to main.kcl',
|
||||||
await expect(fileWithinFolder).not.toBeAttached()
|
async () => {
|
||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await _expect(folderToDelete).not.toBeAttached()
|
||||||
})
|
await _expect(fileWithinFolder).not.toBeAttached()
|
||||||
|
await _expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test.fixme('TODO - delete folder we are in, with no main.kcl', async () => {})
|
_test.fixme(
|
||||||
|
'TODO - delete folder we are in, with no main.kcl',
|
||||||
|
async () => {}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
import type { Page } from '@playwright/test'
|
|
||||||
import { test as base } from '@playwright/test'
|
|
||||||
import { getUtils, setup, tearDown } from './test-utils'
|
|
||||||
import fsp from 'fs/promises'
|
|
||||||
import { join } from 'path'
|
|
||||||
import { CmdBarFixture } from './cmdBarFixture'
|
|
||||||
import { EditorFixture } from './editorFixture'
|
|
||||||
import { ToolbarFixture } from './toolbarFixture'
|
|
||||||
import { SceneFixture } from './sceneFixture'
|
|
||||||
|
|
||||||
export class AuthenticatedApp {
|
|
||||||
public readonly page: Page
|
|
||||||
|
|
||||||
constructor(page: Page) {
|
|
||||||
this.page = page
|
|
||||||
}
|
|
||||||
|
|
||||||
async initialise(code = '') {
|
|
||||||
const u = await getUtils(this.page)
|
|
||||||
|
|
||||||
await this.page.addInitScript(async (code) => {
|
|
||||||
localStorage.setItem('persistCode', code)
|
|
||||||
;(window as any).playwrightSkipFilePicker = true
|
|
||||||
}, code)
|
|
||||||
|
|
||||||
await this.page.setViewportSize({ width: 1000, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
}
|
|
||||||
getInputFile = (fileName: string) => {
|
|
||||||
return fsp.readFile(
|
|
||||||
join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName),
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const test = base.extend<{
|
|
||||||
app: AuthenticatedApp
|
|
||||||
cmdBar: CmdBarFixture
|
|
||||||
editor: EditorFixture
|
|
||||||
toolbar: ToolbarFixture
|
|
||||||
scene: SceneFixture
|
|
||||||
}>({
|
|
||||||
app: async ({ page }, use) => {
|
|
||||||
await use(new AuthenticatedApp(page))
|
|
||||||
},
|
|
||||||
cmdBar: async ({ page }, use) => {
|
|
||||||
await use(new CmdBarFixture(page))
|
|
||||||
},
|
|
||||||
editor: async ({ page }, use) => {
|
|
||||||
await use(new EditorFixture(page))
|
|
||||||
},
|
|
||||||
toolbar: async ({ page }, use) => {
|
|
||||||
await use(new ToolbarFixture(page))
|
|
||||||
},
|
|
||||||
scene: async ({ page }, use) => {
|
|
||||||
await use(new SceneFixture(page))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
|
||||||
await setup(context, page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
|
||||||
await tearDown(page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
export { expect } from '@playwright/test'
|
|
@ -25,11 +25,14 @@ type CmdBarSerialised =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class CmdBarFixture {
|
export class CmdBarFixture {
|
||||||
public readonly page: Page
|
public page: Page
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
}
|
}
|
||||||
|
reConstruct = (page: Page) => {
|
||||||
|
this.page = page
|
||||||
|
}
|
||||||
|
|
||||||
private _serialiseCmdBar = async (): Promise<CmdBarSerialised> => {
|
private _serialiseCmdBar = async (): Promise<CmdBarSerialised> => {
|
||||||
const reviewForm = await this.page.locator('#review-form')
|
const reviewForm = await this.page.locator('#review-form')
|
@ -1,5 +1,6 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
import type { Page, Locator } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
import { sansWhitespace } from '../test-utils'
|
||||||
|
|
||||||
interface EditorState {
|
interface EditorState {
|
||||||
activeLines: Array<string>
|
activeLines: Array<string>
|
||||||
@ -7,19 +8,20 @@ interface EditorState {
|
|||||||
diagnostics: Array<string>
|
diagnostics: Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeWhitespace(str: string) {
|
|
||||||
return str.replace(/\s+/g, '').trim()
|
|
||||||
}
|
|
||||||
export class EditorFixture {
|
export class EditorFixture {
|
||||||
public readonly page: Page
|
public page: Page
|
||||||
|
|
||||||
private readonly diagnosticsTooltip: Locator
|
private diagnosticsTooltip!: Locator
|
||||||
private readonly diagnosticsGutterIcon: Locator
|
private diagnosticsGutterIcon!: Locator
|
||||||
private readonly codeContent: Locator
|
private codeContent!: Locator
|
||||||
private readonly activeLine: Locator
|
private activeLine!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
|
this.reConstruct(page)
|
||||||
|
}
|
||||||
|
reConstruct = (page: Page) => {
|
||||||
|
this.page = page
|
||||||
|
|
||||||
this.codeContent = page.locator('.cm-content')
|
this.codeContent = page.locator('.cm-content')
|
||||||
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
|
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
|
||||||
@ -94,16 +96,16 @@ export class EditorFixture {
|
|||||||
this._serialiseDiagnostics(),
|
this._serialiseDiagnostics(),
|
||||||
])
|
])
|
||||||
const state: EditorState = {
|
const state: EditorState = {
|
||||||
activeLines: activeLines.map(removeWhitespace).filter(Boolean),
|
activeLines: activeLines.map(sansWhitespace).filter(Boolean),
|
||||||
highlightedCode: removeWhitespace(highlightedCode),
|
highlightedCode: sansWhitespace(highlightedCode),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
.toEqual({
|
.toEqual({
|
||||||
activeLines: expectedState.activeLines.map(removeWhitespace),
|
activeLines: expectedState.activeLines.map(sansWhitespace),
|
||||||
highlightedCode: removeWhitespace(expectedState.highlightedCode),
|
highlightedCode: sansWhitespace(expectedState.highlightedCode),
|
||||||
diagnostics: expectedState.diagnostics.map(removeWhitespace),
|
diagnostics: expectedState.diagnostics.map(sansWhitespace),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
140
e2e/playwright/fixtures/fixtureSetup.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import type {
|
||||||
|
BrowserContext,
|
||||||
|
ElectronApplication,
|
||||||
|
Page,
|
||||||
|
TestInfo,
|
||||||
|
} from '@playwright/test'
|
||||||
|
import { test as base } from '@playwright/test'
|
||||||
|
import { getUtils, setup, setupElectron, tearDown } from '../test-utils'
|
||||||
|
import fsp from 'fs/promises'
|
||||||
|
import { join } from 'path'
|
||||||
|
import { CmdBarFixture } from './cmdBarFixture'
|
||||||
|
import { EditorFixture } from './editorFixture'
|
||||||
|
import { ToolbarFixture } from './toolbarFixture'
|
||||||
|
import { SceneFixture } from './sceneFixture'
|
||||||
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||||
|
import { HomePageFixture } from './homePageFixture'
|
||||||
|
import { unsafeTypedKeys } from 'lib/utils'
|
||||||
|
|
||||||
|
export class AuthenticatedApp {
|
||||||
|
public readonly page: Page
|
||||||
|
public readonly context: BrowserContext
|
||||||
|
public readonly testInfo: TestInfo
|
||||||
|
|
||||||
|
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||||
|
this.page = page
|
||||||
|
this.context = context
|
||||||
|
this.testInfo = testInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialise(code = '') {
|
||||||
|
await setup(this.context, this.page, this.testInfo)
|
||||||
|
const u = await getUtils(this.page)
|
||||||
|
|
||||||
|
await this.page.addInitScript(async (code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
;(window as any).playwrightSkipFilePicker = true
|
||||||
|
}, code)
|
||||||
|
|
||||||
|
await this.page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
}
|
||||||
|
getInputFile = (fileName: string) => {
|
||||||
|
return fsp.readFile(
|
||||||
|
join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName),
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Fixtures {
|
||||||
|
app: AuthenticatedApp
|
||||||
|
tronApp: AuthenticatedTronApp
|
||||||
|
cmdBar: CmdBarFixture
|
||||||
|
editor: EditorFixture
|
||||||
|
toolbar: ToolbarFixture
|
||||||
|
scene: SceneFixture
|
||||||
|
homePage: HomePageFixture
|
||||||
|
}
|
||||||
|
export class AuthenticatedTronApp {
|
||||||
|
public readonly _page: Page
|
||||||
|
public page: Page
|
||||||
|
public readonly context: BrowserContext
|
||||||
|
public readonly testInfo: TestInfo
|
||||||
|
public electronApp?: ElectronApplication
|
||||||
|
|
||||||
|
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||||
|
this._page = page
|
||||||
|
this.page = page
|
||||||
|
this.context = context
|
||||||
|
this.testInfo = testInfo
|
||||||
|
}
|
||||||
|
async initialise(
|
||||||
|
arg: {
|
||||||
|
fixtures: Partial<Fixtures>
|
||||||
|
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||||
|
cleanProjectDir?: boolean
|
||||||
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
|
} = { fixtures: {} }
|
||||||
|
) {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo: this.testInfo,
|
||||||
|
folderSetupFn: arg.folderSetupFn,
|
||||||
|
cleanProjectDir: arg.cleanProjectDir,
|
||||||
|
appSettings: arg.appSettings,
|
||||||
|
})
|
||||||
|
this.page = page
|
||||||
|
this.electronApp = electronApp
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
for (const key of unsafeTypedKeys(arg.fixtures)) {
|
||||||
|
const fixture = arg.fixtures[key]
|
||||||
|
if (
|
||||||
|
!fixture ||
|
||||||
|
fixture instanceof AuthenticatedApp ||
|
||||||
|
fixture instanceof AuthenticatedTronApp
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
fixture.reConstruct(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close = async () => {
|
||||||
|
await this.electronApp?.close?.()
|
||||||
|
}
|
||||||
|
debugPause = () =>
|
||||||
|
new Promise(() => {
|
||||||
|
console.log('UN-RESOLVING PROMISE')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const test = base.extend<Fixtures>({
|
||||||
|
app: async ({ page, context }, use, testInfo) => {
|
||||||
|
await use(new AuthenticatedApp(context, page, testInfo))
|
||||||
|
},
|
||||||
|
tronApp: async ({ page, context }, use, testInfo) => {
|
||||||
|
await use(new AuthenticatedTronApp(context, page, testInfo))
|
||||||
|
},
|
||||||
|
cmdBar: async ({ page }, use) => {
|
||||||
|
await use(new CmdBarFixture(page))
|
||||||
|
},
|
||||||
|
editor: async ({ page }, use) => {
|
||||||
|
await use(new EditorFixture(page))
|
||||||
|
},
|
||||||
|
toolbar: async ({ page }, use) => {
|
||||||
|
await use(new ToolbarFixture(page))
|
||||||
|
},
|
||||||
|
scene: async ({ page }, use) => {
|
||||||
|
await use(new SceneFixture(page))
|
||||||
|
},
|
||||||
|
homePage: async ({ page }, use) => {
|
||||||
|
await use(new HomePageFixture(page))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
export { expect } from '@playwright/test'
|
103
e2e/playwright/fixtures/homePageFixture.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import type { Page, Locator } from '@playwright/test'
|
||||||
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
|
interface ProjectCardState {
|
||||||
|
title: string
|
||||||
|
fileCount: number
|
||||||
|
folderCount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HomePageState {
|
||||||
|
projectCards: ProjectCardState[]
|
||||||
|
sortBy: 'last-modified-desc' | 'last-modified-asc' | 'name-asc' | 'name-desc'
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HomePageFixture {
|
||||||
|
public page: Page
|
||||||
|
|
||||||
|
projectCard!: Locator
|
||||||
|
projectCardTitle!: Locator
|
||||||
|
projectCardFile!: Locator
|
||||||
|
projectCardFolder!: Locator
|
||||||
|
sortByDateBtn!: Locator
|
||||||
|
sortByNameBtn!: Locator
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page
|
||||||
|
this.reConstruct(page)
|
||||||
|
}
|
||||||
|
reConstruct = (page: Page) => {
|
||||||
|
this.page = page
|
||||||
|
|
||||||
|
this.projectCard = this.page.getByTestId('project-link')
|
||||||
|
this.projectCardTitle = this.page.getByTestId('project-title')
|
||||||
|
this.projectCardFile = this.page.getByTestId('project-file-count')
|
||||||
|
this.projectCardFolder = this.page.getByTestId('project-folder-count')
|
||||||
|
|
||||||
|
this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified')
|
||||||
|
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serialiseSortBy = async (): Promise<
|
||||||
|
HomePageState['sortBy'] | null
|
||||||
|
> => {
|
||||||
|
const [dateBtnDesc, dateBtnAsc, nameBtnDesc, nameBtnAsc] =
|
||||||
|
await Promise.all([
|
||||||
|
this.sortByDateBtn.getByLabel('arrow down').isVisible(),
|
||||||
|
this.sortByDateBtn.getByLabel('arrow up').isVisible(),
|
||||||
|
this.sortByNameBtn.getByLabel('arrow down').isVisible(),
|
||||||
|
this.sortByNameBtn.getByLabel('arrow up').isVisible(),
|
||||||
|
])
|
||||||
|
if (dateBtnDesc) return 'last-modified-desc'
|
||||||
|
if (dateBtnAsc) return 'last-modified-asc'
|
||||||
|
if (nameBtnDesc) return 'name-desc'
|
||||||
|
if (nameBtnAsc) return 'name-asc'
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serialiseProjectCards = async (): Promise<
|
||||||
|
Array<ProjectCardState>
|
||||||
|
> => {
|
||||||
|
const projectCards = await this.projectCard.all()
|
||||||
|
const projectCardStates: Array<ProjectCardState> = []
|
||||||
|
for (const projectCard of projectCards) {
|
||||||
|
const [title, fileCount, folderCount] = await Promise.all([
|
||||||
|
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
||||||
|
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
||||||
|
Number(await projectCard.locator(this.projectCardFolder).textContent()),
|
||||||
|
])
|
||||||
|
projectCardStates.push({
|
||||||
|
title: title,
|
||||||
|
fileCount,
|
||||||
|
folderCount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return projectCardStates
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date is excluded from expectState, since it changes
|
||||||
|
* Maybe there a good sanity check we can do each time?
|
||||||
|
*/
|
||||||
|
expectState = async (expectedState: HomePageState) => {
|
||||||
|
await expect
|
||||||
|
.poll(async () => {
|
||||||
|
const [projectCards, sortBy] = await Promise.all([
|
||||||
|
this._serialiseProjectCards(),
|
||||||
|
this._serialiseSortBy(),
|
||||||
|
])
|
||||||
|
return {
|
||||||
|
projectCards,
|
||||||
|
sortBy,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toEqual(expectedState)
|
||||||
|
}
|
||||||
|
|
||||||
|
openProject = async (projectTitle: string) => {
|
||||||
|
const projectCard = this.projectCard.locator(
|
||||||
|
this.page.getByText(projectTitle)
|
||||||
|
)
|
||||||
|
await projectCard.click()
|
||||||
|
}
|
||||||
|
}
|
@ -6,18 +6,24 @@ import {
|
|||||||
doAndWaitForImageDiff,
|
doAndWaitForImageDiff,
|
||||||
openAndClearDebugPanel,
|
openAndClearDebugPanel,
|
||||||
sendCustomCmd,
|
sendCustomCmd,
|
||||||
} from './test-utils'
|
} from '../test-utils'
|
||||||
|
|
||||||
type mouseParams = {
|
type mouseParams = {
|
||||||
pixelDiff: number
|
pixelDiff: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SceneFixture {
|
export class SceneFixture {
|
||||||
public readonly page: Page
|
public page: Page
|
||||||
private readonly exeIndicator: Locator
|
|
||||||
|
private exeIndicator!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
|
this.reConstruct(page)
|
||||||
|
}
|
||||||
|
reConstruct = (page: Page) => {
|
||||||
|
this.page = page
|
||||||
|
|
||||||
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,33 +31,38 @@ export class SceneFixture {
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
{ steps }: { steps: number } = { steps: 5000 }
|
{ steps }: { steps: number } = { steps: 5000 }
|
||||||
) => [
|
) =>
|
||||||
(params?: mouseParams) => {
|
[
|
||||||
if (params?.pixelDiff) {
|
(clickParams?: mouseParams) => {
|
||||||
return doAndWaitForImageDiff(
|
if (clickParams?.pixelDiff) {
|
||||||
this.page,
|
return doAndWaitForImageDiff(
|
||||||
() => this.page.mouse.click(x, y),
|
this.page,
|
||||||
params.pixelDiff
|
() => this.page.mouse.click(x, y),
|
||||||
)
|
clickParams.pixelDiff
|
||||||
}
|
)
|
||||||
return this.page.mouse.click(x, y)
|
}
|
||||||
},
|
return this.page.mouse.click(x, y)
|
||||||
(params?: mouseParams) => {
|
},
|
||||||
if (params?.pixelDiff) {
|
(moveParams?: mouseParams) => {
|
||||||
return doAndWaitForImageDiff(
|
if (moveParams?.pixelDiff) {
|
||||||
this.page,
|
return doAndWaitForImageDiff(
|
||||||
() => this.page.mouse.move(x, y, { steps }),
|
this.page,
|
||||||
params.pixelDiff
|
() => this.page.mouse.move(x, y, { steps }),
|
||||||
)
|
moveParams.pixelDiff
|
||||||
}
|
)
|
||||||
return this.page.mouse.move(x, y, { steps })
|
}
|
||||||
},
|
return this.page.mouse.move(x, y, { steps })
|
||||||
]
|
},
|
||||||
|
] as const
|
||||||
|
|
||||||
/** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene.
|
/** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene.
|
||||||
*
|
*
|
||||||
* Expects the viewPort to be 1000x500 */
|
* Expects the viewPort to be 1000x500 */
|
||||||
clickNoWhere = () => this.page.mouse.click(998, 60)
|
clickNoWhere = () => this.page.mouse.click(998, 60)
|
||||||
|
/** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene.
|
||||||
|
*
|
||||||
|
* Expects the viewPort to be 1000x500 */
|
||||||
|
moveNoWhere = (steps?: number) => this.page.mouse.move(998, 60, { steps })
|
||||||
|
|
||||||
moveCameraTo = async (
|
moveCameraTo = async (
|
||||||
pos: { x: number; y: number; z: number },
|
pos: { x: number; y: number; z: number },
|
79
e2e/playwright/fixtures/toolbarFixture.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import type { Page, Locator } from '@playwright/test'
|
||||||
|
import { expect } from './fixtureSetup'
|
||||||
|
import { doAndWaitForImageDiff } from '../test-utils'
|
||||||
|
|
||||||
|
export class ToolbarFixture {
|
||||||
|
public page: Page
|
||||||
|
|
||||||
|
extrudeButton!: Locator
|
||||||
|
startSketchBtn!: Locator
|
||||||
|
rectangleBtn!: Locator
|
||||||
|
exitSketchBtn!: Locator
|
||||||
|
editSketchBtn!: Locator
|
||||||
|
fileTreeBtn!: Locator
|
||||||
|
createFileBtn!: Locator
|
||||||
|
fileCreateToast!: Locator
|
||||||
|
filePane!: Locator
|
||||||
|
exeIndicator!: Locator
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page
|
||||||
|
this.reConstruct(page)
|
||||||
|
}
|
||||||
|
reConstruct = (page: Page) => {
|
||||||
|
this.page = page
|
||||||
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
|
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||||
|
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
||||||
|
this.editSketchBtn = page.getByText('Edit Sketch')
|
||||||
|
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
||||||
|
this.createFileBtn = page.getByTestId('create-file-button')
|
||||||
|
|
||||||
|
this.filePane = page.locator('#files-pane')
|
||||||
|
this.fileCreateToast = page.getByText('Successfully created')
|
||||||
|
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
||||||
|
}
|
||||||
|
|
||||||
|
startSketchPlaneSelection = async () =>
|
||||||
|
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
||||||
|
|
||||||
|
editSketch = async () => {
|
||||||
|
await this.editSketchBtn.first().click()
|
||||||
|
// One of the rare times we want to allow a arbitrary wait
|
||||||
|
// this is for the engine animation, as it takes 500ms to complete
|
||||||
|
await this.page.waitForTimeout(600)
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serialiseFileTree = async () => {
|
||||||
|
return this.page
|
||||||
|
.locator('#files-pane')
|
||||||
|
.getByTestId('file-tree-item')
|
||||||
|
.allInnerTexts()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO folders, in expect state
|
||||||
|
*/
|
||||||
|
expectFileTreeState = async (expected: string[]) => {
|
||||||
|
await expect.poll(this._serialiseFileTree).toEqual(expected)
|
||||||
|
}
|
||||||
|
createFile = async ({ wait }: { wait: boolean } = { wait: false }) => {
|
||||||
|
await this.createFileBtn.click()
|
||||||
|
await expect(this.fileCreateToast).toBeVisible()
|
||||||
|
if (wait) {
|
||||||
|
await this.fileCreateToast.waitFor({ state: 'detached' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Opens file by it's name and waits for execution to finish
|
||||||
|
*/
|
||||||
|
openFile = async (
|
||||||
|
fileName: string,
|
||||||
|
{ wait }: { wait?: boolean } = { wait: true }
|
||||||
|
) => {
|
||||||
|
await this.filePane.getByText(fileName).click()
|
||||||
|
if (wait) {
|
||||||
|
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
e2e/playwright/playwright-deprecated.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
/** @deprecated, import from ./fixtureSetup.ts instead */
|
||||||
|
export const _test = test
|
||||||
|
|
||||||
|
/** @deprecated, import from ./fixtureSetup.ts instead */
|
||||||
|
export const _expect = expect
|
@ -1,7 +1,7 @@
|
|||||||
import { test, expect, AuthenticatedApp } from './fixtureSetup'
|
import { test, expect, AuthenticatedApp } from './fixtures/fixtureSetup'
|
||||||
import { EditorFixture } from './editorFixture'
|
import { EditorFixture } from './fixtures/editorFixture'
|
||||||
import { SceneFixture } from './sceneFixture'
|
import { SceneFixture } from './fixtures/sceneFixture'
|
||||||
import { ToolbarFixture } from './toolbarFixture'
|
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
||||||
|
|
||||||
// test file is for testing point an click code gen functionality that's not sketch mode related
|
// test file is for testing point an click code gen functionality that's not sketch mode related
|
||||||
|
|
||||||
|
@ -455,6 +455,7 @@ test(
|
|||||||
await page.mouse.move(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.move(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
||||||
maxDiffPixels: 100,
|
maxDiffPixels: 100,
|
||||||
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
@ -474,6 +475,7 @@ test(
|
|||||||
|
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
||||||
maxDiffPixels: 100,
|
maxDiffPixels: 100,
|
||||||
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -531,6 +533,7 @@ test(
|
|||||||
// Ensure the draft rectangle looks the same as it usually does
|
// Ensure the draft rectangle looks the same as it usually does
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
||||||
maxDiffPixels: 100,
|
maxDiffPixels: 100,
|
||||||
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -585,6 +588,7 @@ test(
|
|||||||
// Ensure the draft rectangle looks the same as it usually does
|
// Ensure the draft rectangle looks the same as it usually does
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
||||||
maxDiffPixels: 100,
|
maxDiffPixels: 100,
|
||||||
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`const sketch001 = startSketchOn('XZ')
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@ -1066,3 +1066,7 @@ export async function openAndClearDebugPanel(page: Page) {
|
|||||||
await openDebugPanel(page)
|
await openDebugPanel(page)
|
||||||
return clearCommandLogs(page)
|
return clearCommandLogs(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sansWhitespace(str: string) {
|
||||||
|
return str.replace(/\s+/g, '').trim()
|
||||||
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
|
||||||
import { doAndWaitForImageDiff } from './test-utils'
|
|
||||||
|
|
||||||
export class ToolbarFixture {
|
|
||||||
public readonly page: Page
|
|
||||||
readonly extrudeButton: Locator
|
|
||||||
readonly startSketchBtn: Locator
|
|
||||||
readonly rectangleBtn: Locator
|
|
||||||
readonly exitSketchBtn: Locator
|
|
||||||
|
|
||||||
constructor(page: Page) {
|
|
||||||
this.page = page
|
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
|
||||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
|
||||||
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
|
||||||
}
|
|
||||||
|
|
||||||
startSketchPlaneSelection = async () =>
|
|
||||||
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
|
||||||
}
|
|
@ -306,9 +306,19 @@ export class CameraControls {
|
|||||||
event: 'camera_drag_end',
|
event: 'camera_drag_end',
|
||||||
callback: cb,
|
callback: cb,
|
||||||
})
|
})
|
||||||
|
this.engineCommandManager.subscribeToUnreliable({
|
||||||
|
event: 'default_camera_zoom',
|
||||||
|
callback: (a) => {
|
||||||
|
console.log('zoom', a)
|
||||||
|
cb(a)
|
||||||
|
},
|
||||||
|
})
|
||||||
this.engineCommandManager.subscribeTo({
|
this.engineCommandManager.subscribeTo({
|
||||||
event: 'default_camera_zoom',
|
event: 'default_camera_zoom',
|
||||||
callback: cb,
|
callback: (a) => {
|
||||||
|
console.log('zoom', a)
|
||||||
|
cb(a)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
this.engineCommandManager.subscribeTo({
|
this.engineCommandManager.subscribeTo({
|
||||||
event: 'default_camera_get_settings',
|
event: 'default_camera_get_settings',
|
||||||
|
@ -194,7 +194,7 @@ const FileTreeItem = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="contents" ref={itemRef}>
|
<div className="contents" data-testid="file-tree-item" ref={itemRef}>
|
||||||
{fileOrDir.children === null ? (
|
{fileOrDir.children === null ? (
|
||||||
<li
|
<li
|
||||||
className={
|
className={
|
||||||
@ -389,12 +389,14 @@ interface FileTreeProps {
|
|||||||
|
|
||||||
export const FileTreeMenu = () => {
|
export const FileTreeMenu = () => {
|
||||||
const { send } = useFileContext()
|
const { send } = useFileContext()
|
||||||
|
const { send: modelingSend } = useModelingContext()
|
||||||
|
|
||||||
function createFile() {
|
function createFile() {
|
||||||
send({
|
send({
|
||||||
type: 'Create file',
|
type: 'Create file',
|
||||||
data: { name: '', makeDir: false, shouldSetToRename: true },
|
data: { name: '', makeDir: false, shouldSetToRename: true },
|
||||||
})
|
})
|
||||||
|
modelingSend({ type: 'Cancel' })
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFolder() {
|
function createFolder() {
|
||||||
|
@ -104,20 +104,33 @@ function ProjectCard({
|
|||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<h3 className="font-sans relative z-0 p-2">
|
<h3
|
||||||
|
className="font-sans relative z-0 p-2"
|
||||||
|
data-testid="project-title"
|
||||||
|
>
|
||||||
{project.name?.replace(FILE_EXT, '')}
|
{project.name?.replace(FILE_EXT, '')}
|
||||||
</h3>
|
</h3>
|
||||||
)}
|
)}
|
||||||
<span className="px-2 text-chalkboard-60 text-xs">
|
<span className="px-2 text-chalkboard-60 text-xs">
|
||||||
{numberOfFiles} file{numberOfFiles === 1 ? '' : 's'}{' '}
|
<span data-testid="project-file-count">{numberOfFiles}</span> file
|
||||||
{numberOfFolders > 0 &&
|
{numberOfFiles === 1 ? '' : 's'}{' '}
|
||||||
`/ ${numberOfFolders} folder${numberOfFolders === 1 ? '' : 's'}`}
|
{numberOfFolders > 0 && (
|
||||||
|
<>
|
||||||
|
{'/ '}
|
||||||
|
<span data-testid="project-folder-count">
|
||||||
|
{numberOfFolders}
|
||||||
|
</span>{' '}
|
||||||
|
folder{numberOfFolders === 1 ? '' : 's'}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="px-2 text-chalkboard-60 text-xs">
|
<span className="px-2 text-chalkboard-60 text-xs">
|
||||||
Edited{' '}
|
Edited{' '}
|
||||||
{project.metadata && project.metadata.modified
|
<span data-testid="project-edit-date">
|
||||||
? getDisplayedTime(parseInt(project.metadata.modified))
|
{project.metadata && project.metadata.modified
|
||||||
: 'never'}
|
? getDisplayedTime(parseInt(project.metadata.modified))
|
||||||
|
: 'never'}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -1230,7 +1230,7 @@ type ModelTypes = Models['OkModelingCmdResponse_type']['type']
|
|||||||
|
|
||||||
type UnreliableResponses = Extract<
|
type UnreliableResponses = Extract<
|
||||||
Models['OkModelingCmdResponse_type'],
|
Models['OkModelingCmdResponse_type'],
|
||||||
{ type: 'highlight_set_entity' | 'camera_drag_move' }
|
{ type: 'highlight_set_entity' | 'camera_drag_move' | 'default_camera_zoom' }
|
||||||
>
|
>
|
||||||
export interface UnreliableSubscription<T extends UnreliableResponses['type']> {
|
export interface UnreliableSubscription<T extends UnreliableResponses['type']> {
|
||||||
event: T
|
event: T
|
||||||
@ -1572,9 +1572,11 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
'message',
|
'message',
|
||||||
(event: MessageEvent) => {
|
(event: MessageEvent) => {
|
||||||
const result: UnreliableResponses = JSON.parse(event.data)
|
const result: UnreliableResponses = JSON.parse(event.data)
|
||||||
|
console.log('result', result)
|
||||||
Object.values(
|
Object.values(
|
||||||
this.unreliableSubscriptions[result.type] || {}
|
this.unreliableSubscriptions[result.type] || {}
|
||||||
).forEach(
|
).forEach(
|
||||||
|
|
||||||
// TODO: There is only one response that uses the unreliable channel atm,
|
// TODO: There is only one response that uses the unreliable channel atm,
|
||||||
// highlight_set_entity, if there are more it's likely they will all have the same
|
// highlight_set_entity, if there are more it's likely they will all have the same
|
||||||
// sequence logic, but I'm not sure if we use a single global sequence or a sequence
|
// sequence logic, but I'm not sure if we use a single global sequence or a sequence
|
||||||
@ -1933,6 +1935,9 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
) {
|
) {
|
||||||
;(cmd as any).sequence = this.outSequence
|
;(cmd as any).sequence = this.outSequence
|
||||||
this.outSequence++
|
this.outSequence++
|
||||||
|
if(cmd.type === 'default_camera_zoom') {
|
||||||
|
console.log('sending zoom', cmd)
|
||||||
|
}
|
||||||
this.engineConnection?.unreliableSend(command)
|
this.engineConnection?.unreliableSend(command)
|
||||||
return Promise.resolve(null)
|
return Promise.resolve(null)
|
||||||
} else if (
|
} else if (
|
||||||
|
@ -15,6 +15,30 @@ export function isArray(val: any): val is unknown[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* An alternative to `Object.keys()` that returns an array of keys with types.
|
||||||
|
*
|
||||||
|
* It's UNSAFE because because of TS's structural subtyping and how at runtime, you can
|
||||||
|
* extend a JS object with whatever keys you want.
|
||||||
|
*
|
||||||
|
* Why we shouldn't be extending objects with arbitrary keys at run time, the structural subtyping
|
||||||
|
* issue could be a confusing bug, for example, in the below snippet `myKeys` is typed as
|
||||||
|
* `('x' | 'y')[]` but is really `('x' | 'y' | 'name')[]`
|
||||||
|
* ```ts
|
||||||
|
* interface Point { x: number; y: number }
|
||||||
|
* interface NamedPoint { x: number; y: number; name: string }
|
||||||
|
*
|
||||||
|
* let point: Point = { x: 1, y: 2 }
|
||||||
|
* let namedPoint: NamedPoint = { x: 1, y: 2, name: 'A' }
|
||||||
|
*
|
||||||
|
* // Structural subtyping allows this assignment
|
||||||
|
* point = namedPoint // This is allowed because NamedPoint has all properties of Point
|
||||||
|
* const myKeys = unsafeTypedKeys(point) // typed as ('x' | 'y')[] but is really ('x' | 'y' | 'name')[]
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function unsafeTypedKeys<T extends object>(obj: T): Array<keyof T> {
|
||||||
|
return Object.keys(obj) as Array<keyof T>
|
||||||
|
}
|
||||||
|
/*
|
||||||
* Predicate that checks if a value is not null and not undefined. This is
|
* Predicate that checks if a value is not null and not undefined. This is
|
||||||
* useful for functions like Array::filter() and Array::find() that have
|
* useful for functions like Array::filter() and Array::find() that have
|
||||||
* overloads that accept a type guard.
|
* overloads that accept a type guard.
|
||||||
|
@ -78,7 +78,7 @@ export const authMachine = setup({
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
}).createMachine({
|
}).createMachine({
|
||||||
/** @xstate-layout N4IgpgJg5mDOIC5QEECuAXAFgOgMabFwGsBJAMwBkB7KGCEgOwGIIqGxsBLBgNyqI75CRALQAbGnRHcA2gAYAuolAAHKrE7pObZSAAeiAIwBmAEzYA7ABYAbAFZTcgBzGbN44adWANCACeiKbGdthypk4AnBFyVs6uQXYAvom+aFh4BMTk1LSQjExgAE6FVIXYKmIAhuhkpQC2GcLikpDSDPJKSCBqGlo6XQYIrk7YETYWctYRxmMWFk6+AUPj2I5OdjZyrnZOFmbJqRg4Ern0zDkABFQYHbo9mtoMuoOGFhHYxlZOhvbOsUGGRaIL4WbBONzWQxWYwWOx2H4HEBpY4tCAAeQwTEuskUd3UD36oEGIlMNlCuzk8Js0TcVisgP8iG2lmcGysb0mW3ByRSIAYVAgcF0yLxvUez0QIms5ImVJpNjpDKWxmw9PGdLh4Te00+iORjSylFRjFFBKeA0QThGQWcexMwWhniBCGiqrepisUVMdlszgieqO2BOdBNXXufXNRKMHtGVuphlJkXs4Wdriso2CCasdgipOidID6WDkAx6FNEYlCAT5jmcjrckMdj2b3GzpsjbBMVMWezDbGPMSQA */
|
/** @xstate-layout N4IgpgJg5mDOIC5QEECuAXAFgOgMabFwGsBJAMwBkB7KGCEgOwGIIqGxsBLBgNyqI75CRALQAbGnRHcA2gAYAuolAAHKrE7pObZSAAeiAIwAWQ9gBspuQCYAnAGYAHPYCsx+4ccAaEAE9E1q7YcoZyxrYR1m7mcrYAvnE+aFh4BMTk1LSQjExgAE55VHnYKmIAhuhkRQC2qcLikpDSDPJKSCBqGlo67QYI9gDs5tge5o6h5vau7oY+-v3mA9jWco4u5iu21ua2YcYJSRg4Eln0zJkABFQYrbqdmtoMun2GA7YjxuPmLqvGNh5zRCfJaOcyLUzuAYuFyGcwHEDJY6NCAAeQwTEuskUd3UDx6oD6Im2wUcAzkMJ2cjBxlMgIWLmwZLWljecjJTjh8IYVAgcF0iJxXUez0QIgGxhJZIpu2ptL8AWwtje1nCW2iq1shns8MRdXSlGRjEFeKevUQjkcy3sqwGHimbg83nlCF22GMytVUWMMUc8USCKO2BOdCN7Xu3VNBKMKsVFp2hm2vu+1id83slkVrgTxhcW0pNJ1geDkDR6GNEZFCAT1kZZLk9cMLltb0WdPMjewjjC1mzOZCtk5CSAA */
|
||||||
id: 'Auth',
|
id: 'Auth',
|
||||||
initial: 'checkIfLoggedIn',
|
initial: 'checkIfLoggedIn',
|
||||||
context: {
|
context: {
|
||||||
@ -162,15 +162,15 @@ async function getUser(input: { token?: string }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPromise = !isDesktop()
|
const userPromise = isDesktop()
|
||||||
? fetch(url, {
|
? getUserDesktop(token, VITE_KC_API_BASE_URL)
|
||||||
|
: fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers,
|
headers,
|
||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.catch((err) => console.error('error from Browser getUser', err))
|
.catch((err) => console.error('error from Browser getUser', err))
|
||||||
: getUserDesktop(input.token ?? '', VITE_KC_API_BASE_URL)
|
|
||||||
|
|
||||||
const user = await userPromise
|
const user = await userPromise
|
||||||
|
|
||||||
|
@ -248,6 +248,7 @@ const Home = () => {
|
|||||||
<small>Sort by</small>
|
<small>Sort by</small>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
|
data-testid="home-sort-by-name"
|
||||||
className={
|
className={
|
||||||
'text-xs border-primary/10 ' +
|
'text-xs border-primary/10 ' +
|
||||||
(!sort.includes('name')
|
(!sort.includes('name')
|
||||||
@ -269,6 +270,7 @@ const Home = () => {
|
|||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
|
data-testid="home-sort-by-modified"
|
||||||
className={
|
className={
|
||||||
'text-xs border-primary/10 ' +
|
'text-xs border-primary/10 ' +
|
||||||
(!isSortByModified
|
(!isSortByModified
|
||||||
|
1271
src/wasm-lib/Cargo.lock
generated
@ -24,12 +24,11 @@ wasm-bindgen-futures = "0.4.42"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
hyper = { version = "0.14.29", features = ["server", "http1"] }
|
|
||||||
image = { version = "0.25.1", default-features = false, features = ["png"] }
|
image = { version = "0.25.1", default-features = false, features = ["png"] }
|
||||||
kittycad = { workspace = true, default-features = true }
|
kittycad = { workspace = true, default-features = true }
|
||||||
kittycad-modeling-cmds = { workspace = true }
|
kittycad-modeling-cmds = { workspace = true }
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
||||||
reqwest = { version = "0.11.26", default-features = false }
|
reqwest = { version = "0.12", default-features = false }
|
||||||
tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "time"] }
|
tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||||
twenty-twenty = "0.8"
|
twenty-twenty = "0.8"
|
||||||
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
|
||||||
@ -67,12 +66,12 @@ members = [
|
|||||||
"kcl",
|
"kcl",
|
||||||
"kcl-macros",
|
"kcl-macros",
|
||||||
"kcl-test-server",
|
"kcl-test-server",
|
||||||
|
"kcl-to-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
http = "0.2.12"
|
http = "1"
|
||||||
kittycad = { version = "0.3.20", default-features = false, features = ["js", "requests"] }
|
kittycad = { version = "0.3.23", default-features = false, features = ["js", "requests"] }
|
||||||
kittycad-modeling-session = "0.1.4"
|
|
||||||
kittycad-modeling-cmds = { version = "0.2.64", features = ["websocket"] }
|
kittycad-modeling-cmds = { version = "0.2.64", features = ["websocket"] }
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
|
@ -20,7 +20,7 @@ quote = "1"
|
|||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
serde_tokenstream = "0.2"
|
serde_tokenstream = "0.2"
|
||||||
syn = { version = "2.0.77", features = ["full"] }
|
syn = { version = "2.0.79", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1.0.89"
|
anyhow = "1.0.89"
|
||||||
|
@ -758,7 +758,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_someFn {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_someFn {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_show {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ mod test_examples_show {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_show {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_my_func {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ mod test_examples_my_func {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_line_to {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ mod test_examples_line_to {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_min {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ mod test_examples_min {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_show {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_import {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_import {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_import {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_show {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_some_function {
|
|||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None).await.unwrap();
|
ctx.run(&program, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ databake = "0.1.8"
|
|||||||
kcl-lib = { path = "../kcl" }
|
kcl-lib = { path = "../kcl" }
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "2.0.77", features = ["full"] }
|
syn = { version = "2.0.79", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
||||||
|
@ -7,7 +7,7 @@ license = "MIT"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.89"
|
anyhow = "1.0.89"
|
||||||
hyper = { version = "0.14.29", features = ["server"] }
|
hyper = { version = "0.14.29", features = ["http1", "server", "tcp"] }
|
||||||
kcl-lib = { version = "0.2", path = "../kcl" }
|
kcl-lib = { version = "0.2", path = "../kcl" }
|
||||||
pico-args = "0.5.0"
|
pico-args = "0.5.0"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
|
23
src/wasm-lib/kcl-to-core/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "kcl-to-core"
|
||||||
|
description = "Utility methods to convert kcl to engine core executable tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "kcl-to-core"
|
||||||
|
path = "src/tool.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1"
|
||||||
|
async-trait = "0.1.81"
|
||||||
|
indexmap = "2.5.0"
|
||||||
|
kcl-lib = { path = "../kcl" }
|
||||||
|
kittycad = { workspace = true, features = ["clap"] }
|
||||||
|
kittycad-modeling-cmds = { workspace = true }
|
||||||
|
tokio = { version = "1.38", features = ["full", "time", "rt", "tracing"] }
|
||||||
|
uuid = { version = "1.9.1", features = ["v4", "js", "serde"] }
|
469
src/wasm-lib/kcl-to-core/src/conn_mock_core.rs
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use kcl_lib::{errors::KclError, executor::DefaultPlanes};
|
||||||
|
use kittycad_modeling_cmds::{
|
||||||
|
self as kcmc,
|
||||||
|
id::ModelingCmdId,
|
||||||
|
ok_response::OkModelingCmdResponse,
|
||||||
|
shared::PathSegment::{self, *},
|
||||||
|
websocket::{ModelingBatch, ModelingCmdReq, OkWebSocketResponseData, WebSocketRequest, WebSocketResponse},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
const CPP_PREFIX: &str = "const double scaleFactor = 100;\n";
|
||||||
|
const NEED_PLANES: bool = true;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EngineConnection {
|
||||||
|
batch: Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::executor::SourceRange)>>>,
|
||||||
|
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::executor::SourceRange)>>>,
|
||||||
|
core_test: Arc<Mutex<String>>,
|
||||||
|
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EngineConnection {
|
||||||
|
pub async fn new(result: Arc<Mutex<String>>) -> Result<EngineConnection> {
|
||||||
|
if let Ok(mut code) = result.lock() {
|
||||||
|
code.push_str(CPP_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EngineConnection {
|
||||||
|
batch: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||||
|
core_test: result,
|
||||||
|
default_planes: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_command(&self, cmd_id: &ModelingCmdId, cmd: &kcmc::ModelingCmd) -> (String, OkModelingCmdResponse) {
|
||||||
|
let cpp_id = id_to_cpp(cmd_id);
|
||||||
|
let cmd_id = format!("{}", cmd_id);
|
||||||
|
let mut this_response = OkModelingCmdResponse::Empty {};
|
||||||
|
|
||||||
|
let new_code = match cmd {
|
||||||
|
kcmc::ModelingCmd::ObjectVisible(kcmc::ObjectVisible { hidden, object_id }) => {
|
||||||
|
format!(r#"scene->getSceneObject(Utils::UUID("{object_id}"))->setHidden({hidden});"#)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::EnableSketchMode(kcmc::EnableSketchMode {
|
||||||
|
entity_id,
|
||||||
|
animated: _,
|
||||||
|
ortho: _,
|
||||||
|
adjust_camera: _,
|
||||||
|
planar_normal,
|
||||||
|
}) => {
|
||||||
|
if let Some(normal) = planar_normal {
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
if(!scene->enableSketchMode(Utils::UUID("{entity_id}"), glm::dvec3 {{ {}, {}, {}, }}, nullopt))
|
||||||
|
{{
|
||||||
|
Utils::Plane plane_{cpp_id}(glm::dvec3 {{ 0, 0, 0 }}, glm::dvec3 {{ 1, 0, 0 }}, glm::dvec3 {{ 0, 1, 0 }});
|
||||||
|
scene->enableSketchMode(plane_{cpp_id}, nullopt, nullopt, false);
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
normal.x, normal.y, normal.z
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::SketchModeDisable(kcmc::SketchModeDisable {}) => "scene->disableSketchMode();".into(),
|
||||||
|
kcmc::ModelingCmd::MakePlane(kcmc::MakePlane {
|
||||||
|
origin,
|
||||||
|
x_axis,
|
||||||
|
y_axis,
|
||||||
|
size,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let plane_id = format!("plane_{}", cpp_id);
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
auto {plane_id} = make_shared<Object>("plane", glm::dvec3 {{ 0, 0, 0 }});
|
||||||
|
{plane_id}->setUUID(Utils::UUID("{cmd_id}"));
|
||||||
|
{plane_id}->makePlane(glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }}, glm::dvec3 {{ {}, {}, {} }}, {}, false);
|
||||||
|
{plane_id}->setHidden();
|
||||||
|
scene->addSceneObject({plane_id});
|
||||||
|
"#,
|
||||||
|
origin.x.0,
|
||||||
|
origin.y.0,
|
||||||
|
origin.z.0,
|
||||||
|
x_axis.x,
|
||||||
|
x_axis.y,
|
||||||
|
x_axis.z,
|
||||||
|
y_axis.x,
|
||||||
|
y_axis.y,
|
||||||
|
y_axis.z,
|
||||||
|
size.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::StartPath(kcmc::StartPath {}) => {
|
||||||
|
let sketch_id = format!("sketch_{}", cpp_id);
|
||||||
|
let path_id = format!("path_{}", cpp_id);
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
auto {sketch_id} = make_shared<Object>("sketch", glm::dvec3 {{ 0, 0, 0 }});
|
||||||
|
{sketch_id}->setUUID(Utils::UUID("{cmd_id}"));
|
||||||
|
{sketch_id}->makePath(true);
|
||||||
|
auto {path_id} = {sketch_id}->get<Model::Brep::Path>();
|
||||||
|
scene->addSceneObject({sketch_id});
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::MovePathPen(kcmc::MovePathPen { path, to }) => {
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
path_{}->moveTo(glm::dvec3 {{ {}, {}, 0.0 }} * scaleFactor);
|
||||||
|
"#,
|
||||||
|
id_to_cpp(path),
|
||||||
|
to.x.0,
|
||||||
|
to.y.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::ExtendPath(kcmc::ExtendPath { path, segment }) => match segment {
|
||||||
|
Line { end, relative } => {
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
path_{}->lineTo(glm::dvec3 {{ {}, {}, 0.0 }} * scaleFactor, {{ {} }});
|
||||||
|
"#,
|
||||||
|
id_to_cpp(path),
|
||||||
|
end.x.0,
|
||||||
|
end.y.0,
|
||||||
|
relative
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PathSegment::Arc {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
relative,
|
||||||
|
} => {
|
||||||
|
let start = start.value;
|
||||||
|
let end = end.value;
|
||||||
|
let radius = radius.0;
|
||||||
|
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
path_{}->addArc(glm::dvec2 {{ {}, {} }} * scaleFactor, {radius} * scaleFactor, {start}, {end}, {{ {} }});
|
||||||
|
"#,
|
||||||
|
id_to_cpp(path),
|
||||||
|
center.x.0,
|
||||||
|
center.y.0,
|
||||||
|
relative
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PathSegment::TangentialArcTo {
|
||||||
|
angle_snap_increment: _,
|
||||||
|
to,
|
||||||
|
} => {
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
path_{}->tangentialArcTo(glm::dvec3 {{ {}, {}, {} }} * scaleFactor, nullopt, {{ true }});
|
||||||
|
"#,
|
||||||
|
id_to_cpp(path),
|
||||||
|
to.x.0,
|
||||||
|
to.y.0,
|
||||||
|
to.z.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
format!("//{:?}", cmd)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
kcmc::ModelingCmd::ClosePath(kcmc::ClosePath { path_id }) => {
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
path_{}->close();
|
||||||
|
sketch_{}->toSolid2D();
|
||||||
|
"#,
|
||||||
|
uuid_to_cpp(path_id),
|
||||||
|
uuid_to_cpp(path_id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::Extrude(kcmc::Extrude { distance, target }) => {
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
scene->getSceneObject(Utils::UUID("{target}"))->extrudeToSolid3D({} * scaleFactor, true);
|
||||||
|
"#,
|
||||||
|
distance.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::Revolve(kcmc::Revolve {
|
||||||
|
angle,
|
||||||
|
axis,
|
||||||
|
axis_is_2d,
|
||||||
|
origin,
|
||||||
|
target,
|
||||||
|
tolerance,
|
||||||
|
}) => {
|
||||||
|
let ox = origin.x.0;
|
||||||
|
let oy = origin.y.0;
|
||||||
|
let oz = origin.z.0;
|
||||||
|
let ax = axis.x;
|
||||||
|
let ay = axis.y;
|
||||||
|
let az = axis.z;
|
||||||
|
let angle = angle.value;
|
||||||
|
let tolerance = tolerance.0;
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
scene->getSceneObject(Utils::UUID("{target}"))->revolveToSolid3D(nullopt, glm::dvec3 {{ {ox}, {oy}, {oz} }} * scaleFactor, glm::dvec3 {{ {ax}, {ay}, {az} }}, {axis_is_2d}, {angle}, {tolerance});
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::Solid2dAddHole(kcmc::Solid2dAddHole { hole_id, object_id }) => {
|
||||||
|
format!(
|
||||||
|
r#"scene->getSceneObject(Utils::UUID("{object_id}"))->get<Model::Brep::Solid2D>()->addHole(
|
||||||
|
make_shared<Model::Brep::Path>(*scene->getSceneObject(Utils::UUID("{hole_id}"))->get<Model::Brep::Solid2D>()->getPath())
|
||||||
|
);"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::Solid3dGetExtrusionFaceInfo(kcmc::Solid3dGetExtrusionFaceInfo {
|
||||||
|
object_id,
|
||||||
|
edge_id,
|
||||||
|
}) => {
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
//face info get {} {}
|
||||||
|
"#,
|
||||||
|
object_id, edge_id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::EntityCircularPattern(kcmc::EntityCircularPattern {
|
||||||
|
entity_id,
|
||||||
|
axis,
|
||||||
|
center,
|
||||||
|
num_repetitions,
|
||||||
|
arc_degrees,
|
||||||
|
rotate_duplicates,
|
||||||
|
}) => {
|
||||||
|
let entity_ids = generate_repl_uuids(*num_repetitions as usize);
|
||||||
|
|
||||||
|
this_response = OkModelingCmdResponse::EntityCircularPattern(kcmc::output::EntityCircularPattern {
|
||||||
|
entity_ids: entity_ids.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut base_code: String = format!(
|
||||||
|
r#"
|
||||||
|
auto reps_{cpp_id} = scene->entityCircularPattern(Utils::UUID("{}"), {num_repetitions}, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, {arc_degrees}, {rotate_duplicates});
|
||||||
|
"#,
|
||||||
|
entity_id, axis.x, axis.y, axis.z, center.x.0, center.y.0, center.z.0
|
||||||
|
);
|
||||||
|
|
||||||
|
let repl_uuid_fix_code = codegen_cpp_repl_uuid_setters(&cpp_id, &entity_ids);
|
||||||
|
base_code.push_str(&repl_uuid_fix_code);
|
||||||
|
|
||||||
|
base_code
|
||||||
|
}
|
||||||
|
kcmc::ModelingCmd::EntityLinearPattern(kcmc::EntityLinearPattern {
|
||||||
|
entity_id: _,
|
||||||
|
axis: _,
|
||||||
|
num_repetitions: _,
|
||||||
|
spacing: _,
|
||||||
|
}) => {
|
||||||
|
// let num_transforms = transforms.len();
|
||||||
|
// let num_repetitions = transform.iter().map(|t| if t.replicate { 1 } else { 0 } ).sum();
|
||||||
|
|
||||||
|
// let mut base_code: String = format!(
|
||||||
|
// r#"
|
||||||
|
// std::vector<std::optional<Scene::Scene::LinearPatternTransform>> transforms_{cpp_id}({num_transforms});
|
||||||
|
// "#);
|
||||||
|
|
||||||
|
// for t in transform {
|
||||||
|
// translations_xyz.push(t.translate.x.to_millimeters(state.units));
|
||||||
|
// translations_xyz.push(t.translate.y.to_millimeters(state.units));
|
||||||
|
// translations_xyz.push(t.translate.z.to_millimeters(state.units));
|
||||||
|
// scale_xyz.push(t.scale.x);
|
||||||
|
// scale_xyz.push(t.scale.y);
|
||||||
|
// scale_xyz.push(t.scale.z);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let entity_ids = generate_repl_uuids(*num_repetitions as usize);
|
||||||
|
|
||||||
|
// this_response = OkModelingCmdResponse::EntityLinearPattern {
|
||||||
|
// data: kittycad::types::EntityLinearPattern {
|
||||||
|
// entity_ids: entity_ids.clone(),
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let mut base_code: String = format!(
|
||||||
|
// r#"
|
||||||
|
// auto reps_{cpp_id} = scene->entityCircularPattern(Utils::UUID("{}"), {num_repetitions}, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, {arc_degrees}, {rotate_duplicates});
|
||||||
|
// "#,
|
||||||
|
// entity_id, axis.x, axis.y, axis.z, center.x, center.y, center.z
|
||||||
|
// );
|
||||||
|
|
||||||
|
// let repl_uuid_fix_code = codegen_cpp_repl_uuid_setters(&cpp_id, &entity_ids);
|
||||||
|
// base_code.push_str(&repl_uuid_fix_code);
|
||||||
|
|
||||||
|
// base_code
|
||||||
|
format!("//{:?}", cmd)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
//helps us follow along with the currently unhandled engine commands
|
||||||
|
format!("//{:?}", cmd)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(new_code, this_response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id_to_cpp(id: &ModelingCmdId) -> String {
|
||||||
|
uuid_to_cpp(&id.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uuid_to_cpp(id: &uuid::Uuid) -> String {
|
||||||
|
let str = format!("{}", id);
|
||||||
|
str::replace(&str, "-", "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_repl_uuids(count: usize) -> Vec<uuid::Uuid> {
|
||||||
|
let mut repl_ids: Vec<uuid::Uuid> = Vec::new();
|
||||||
|
|
||||||
|
repl_ids.resize_with(count, uuid::Uuid::new_v4);
|
||||||
|
repl_ids
|
||||||
|
}
|
||||||
|
|
||||||
|
fn codegen_cpp_repl_uuid_setters(reps_id: &str, entity_ids: &[uuid::Uuid]) -> String {
|
||||||
|
let mut codegen = String::new();
|
||||||
|
|
||||||
|
for (i, id) in entity_ids.iter().enumerate() {
|
||||||
|
let cpp_id = uuid_to_cpp(id);
|
||||||
|
let iter = format!(
|
||||||
|
r#"
|
||||||
|
//change object id -> {id}
|
||||||
|
auto repl_{cpp_id} = scene->getSceneObject(reps_{reps_id}[{i}]);
|
||||||
|
scene->removeSceneObject(repl_{cpp_id}->getUUID(), false);
|
||||||
|
repl_{cpp_id}->setUUID(Utils::UUID("{id}"));
|
||||||
|
scene->addSceneObject(repl_{cpp_id});
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
codegen.push_str(&iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl kcl_lib::engine::EngineManager for EngineConnection {
|
||||||
|
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::executor::SourceRange)>>> {
|
||||||
|
self.batch.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::executor::SourceRange)>>> {
|
||||||
|
self.batch_end.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn default_planes(&self, source_range: kcl_lib::executor::SourceRange) -> Result<DefaultPlanes, KclError> {
|
||||||
|
if NEED_PLANES {
|
||||||
|
{
|
||||||
|
let opt = self.default_planes.read().await.as_ref().cloned();
|
||||||
|
if let Some(planes) = opt {
|
||||||
|
return Ok(planes);
|
||||||
|
}
|
||||||
|
} // drop the read lock
|
||||||
|
|
||||||
|
let new_planes = self.new_default_planes(source_range).await?;
|
||||||
|
*self.default_planes.write().await = Some(new_planes.clone());
|
||||||
|
|
||||||
|
Ok(new_planes)
|
||||||
|
} else {
|
||||||
|
Ok(DefaultPlanes::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn clear_scene_post_hook(&self, _source_range: kcl_lib::executor::SourceRange) -> Result<(), KclError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inner_send_modeling_cmd(
|
||||||
|
&self,
|
||||||
|
id: uuid::Uuid,
|
||||||
|
_source_range: kcl_lib::executor::SourceRange,
|
||||||
|
cmd: WebSocketRequest,
|
||||||
|
_id_to_source_range: std::collections::HashMap<uuid::Uuid, kcl_lib::executor::SourceRange>,
|
||||||
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
|
match cmd {
|
||||||
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
|
||||||
|
ref requests,
|
||||||
|
batch_id: _,
|
||||||
|
responses: _,
|
||||||
|
}) => {
|
||||||
|
let mut responses = HashMap::new();
|
||||||
|
for request in requests {
|
||||||
|
let (new_code, this_response);
|
||||||
|
|
||||||
|
if let Ok(mut test_code) = self.core_test.lock() {
|
||||||
|
(new_code, this_response) = self.handle_command(&request.cmd_id, &request.cmd);
|
||||||
|
|
||||||
|
if !new_code.is_empty() {
|
||||||
|
let new_code = new_code
|
||||||
|
.trim()
|
||||||
|
.split(' ')
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
+ "\n";
|
||||||
|
//println!("{new_code}");
|
||||||
|
test_code.push_str(&new_code);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this_response = OkModelingCmdResponse::Empty {};
|
||||||
|
}
|
||||||
|
|
||||||
|
responses.insert(
|
||||||
|
request.cmd_id,
|
||||||
|
kcmc::websocket::BatchResponse::Success {
|
||||||
|
response: this_response,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
|
||||||
|
success: true,
|
||||||
|
request_id: Some(id),
|
||||||
|
resp: OkWebSocketResponseData::ModelingBatch { responses },
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd, cmd_id }) => {
|
||||||
|
//also handle unbatched requests inline
|
||||||
|
let (new_code, this_response);
|
||||||
|
|
||||||
|
if let Ok(mut test_code) = self.core_test.lock() {
|
||||||
|
(new_code, this_response) = self.handle_command(&cmd_id, &cmd);
|
||||||
|
|
||||||
|
if !new_code.is_empty() {
|
||||||
|
let new_code = new_code
|
||||||
|
.trim()
|
||||||
|
.split(' ')
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
+ "\n";
|
||||||
|
//println!("{new_code}");
|
||||||
|
test_code.push_str(&new_code);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this_response = OkModelingCmdResponse::Empty {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
|
||||||
|
success: true,
|
||||||
|
request_id: Some(id),
|
||||||
|
resp: OkWebSocketResponseData::Modeling {
|
||||||
|
modeling_response: this_response,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
_ => Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
|
||||||
|
success: true,
|
||||||
|
request_id: Some(id),
|
||||||
|
resp: OkWebSocketResponseData::Modeling {
|
||||||
|
modeling_response: OkModelingCmdResponse::Empty {},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/wasm-lib/kcl-to-core/src/lib.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use kcl_lib::executor::ExecutorContext;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod conn_mock_core;
|
||||||
|
|
||||||
|
///Converts the given kcl code to an engine test
|
||||||
|
pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
||||||
|
let tokens = kcl_lib::token::lexer(code)?;
|
||||||
|
let parser = kcl_lib::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast()?;
|
||||||
|
|
||||||
|
let result = Arc::new(Mutex::new("".into()));
|
||||||
|
let ref_result = Arc::clone(&result);
|
||||||
|
|
||||||
|
let ctx = ExecutorContext {
|
||||||
|
engine: Arc::new(Box::new(
|
||||||
|
crate::conn_mock_core::EngineConnection::new(ref_result).await?,
|
||||||
|
)),
|
||||||
|
fs: Arc::new(kcl_lib::fs::FileManager::new()),
|
||||||
|
stdlib: Arc::new(kcl_lib::std::StdLib::new()),
|
||||||
|
settings: Default::default(),
|
||||||
|
context_type: kcl_lib::executor::ContextType::MockCustomForwarded,
|
||||||
|
};
|
||||||
|
let _memory = ctx.run(&program, None).await?;
|
||||||
|
|
||||||
|
let result = result.lock().expect("mutex lock").clone();
|
||||||
|
Ok(result)
|
||||||
|
}
|
19
src/wasm-lib/kcl-to-core/src/tool.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use kcl_to_core::*;
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
if args.len() < 2 {
|
||||||
|
println!("Usage: kcl-to-core path/to/file.kcl");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_path = &args[1];
|
||||||
|
let kcl = fs::read_to_string(file_path).expect("read file");
|
||||||
|
|
||||||
|
let result = kcl_to_engine_core(&kcl).await.expect("kcl conversion");
|
||||||
|
|
||||||
|
println!("{}", result);
|
||||||
|
}
|
19
src/wasm-lib/kcl-to-core/tests/kcl_to_core_test.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use kcl_to_core::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn kcl_to_core_test() {
|
||||||
|
let result = kcl_to_engine_core(
|
||||||
|
r#"
|
||||||
|
const part001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([11.19, 28.35], %)
|
||||||
|
|> line([28.67, -13.25], %, $here)
|
||||||
|
|> line([-4.12, -22.81], %)
|
||||||
|
|> line([-33.24, 14.55], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
@ -35,7 +35,7 @@ measurements = "0.11.0"
|
|||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
parse-display = "0.9.1"
|
parse-display = "0.9.1"
|
||||||
pyo3 = { version = "0.22.3", optional = true }
|
pyo3 = { version = "0.22.3", optional = true }
|
||||||
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] }
|
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
|
||||||
ropey = "1.6.1"
|
ropey = "1.6.1"
|
||||||
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1", "preserve_order"] }
|
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1", "preserve_order"] }
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
|
@ -18,7 +18,11 @@ use tower_lsp::lsp_types::{
|
|||||||
CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange, FoldingRangeKind, Range as LspRange, SymbolKind,
|
CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange, FoldingRangeKind, Range as LspRange, SymbolKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::ast::types::{literal_value::LiteralValue, none::KclNone};
|
pub use crate::ast::types::{
|
||||||
|
condition::{ElseIf, IfExpression},
|
||||||
|
literal_value::LiteralValue,
|
||||||
|
none::KclNone,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
@ -30,12 +34,14 @@ use crate::{
|
|||||||
std::{kcl_stdlib::KclStdLibFn, FunctionKind},
|
std::{kcl_stdlib::KclStdLibFn, FunctionKind},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod condition;
|
||||||
mod literal_value;
|
mod literal_value;
|
||||||
mod none;
|
mod none;
|
||||||
|
|
||||||
/// Position-independent digest of the AST node.
|
/// Position-independent digest of the AST node.
|
||||||
pub type Digest = [u8; 32];
|
pub type Digest = [u8; 32];
|
||||||
|
|
||||||
|
/// A KCL program top level, or function body.
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
#[databake(path = kcl_lib::ast::types)]
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -72,6 +78,7 @@ macro_rules! compute_digest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub(crate) use compute_digest;
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
compute_digest!(|slf, hasher| {
|
compute_digest!(|slf, hasher| {
|
||||||
@ -82,6 +89,14 @@ impl Program {
|
|||||||
hasher.update(slf.non_code_meta.compute_digest());
|
hasher.update(slf.non_code_meta.compute_digest());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Is the last body item an expression?
|
||||||
|
pub fn ends_with_expr(&self) -> bool {
|
||||||
|
let Some(ref last) = self.body.last() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
matches!(last, BodyItem::ExpressionStatement(_))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||||
// Check if we are in the non code meta.
|
// Check if we are in the non code meta.
|
||||||
if let Some(meta) = self.get_non_code_meta_for_position(pos) {
|
if let Some(meta) = self.get_non_code_meta_for_position(pos) {
|
||||||
@ -501,6 +516,7 @@ pub enum Expr {
|
|||||||
ObjectExpression(Box<ObjectExpression>),
|
ObjectExpression(Box<ObjectExpression>),
|
||||||
MemberExpression(Box<MemberExpression>),
|
MemberExpression(Box<MemberExpression>),
|
||||||
UnaryExpression(Box<UnaryExpression>),
|
UnaryExpression(Box<UnaryExpression>),
|
||||||
|
IfExpression(Box<IfExpression>),
|
||||||
None(KclNone),
|
None(KclNone),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,6 +535,7 @@ impl Expr {
|
|||||||
Expr::ObjectExpression(oe) => oe.compute_digest(),
|
Expr::ObjectExpression(oe) => oe.compute_digest(),
|
||||||
Expr::MemberExpression(me) => me.compute_digest(),
|
Expr::MemberExpression(me) => me.compute_digest(),
|
||||||
Expr::UnaryExpression(ue) => ue.compute_digest(),
|
Expr::UnaryExpression(ue) => ue.compute_digest(),
|
||||||
|
Expr::IfExpression(e) => e.compute_digest(),
|
||||||
Expr::None(_) => {
|
Expr::None(_) => {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(b"Value::None");
|
hasher.update(b"Value::None");
|
||||||
@ -562,6 +579,7 @@ impl Expr {
|
|||||||
Expr::PipeExpression(pipe_exp) => Some(&pipe_exp.non_code_meta),
|
Expr::PipeExpression(pipe_exp) => Some(&pipe_exp.non_code_meta),
|
||||||
Expr::UnaryExpression(_unary_exp) => None,
|
Expr::UnaryExpression(_unary_exp) => None,
|
||||||
Expr::PipeSubstitution(_pipe_substitution) => None,
|
Expr::PipeSubstitution(_pipe_substitution) => None,
|
||||||
|
Expr::IfExpression(_) => None,
|
||||||
Expr::None(_none) => None,
|
Expr::None(_none) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -584,6 +602,7 @@ impl Expr {
|
|||||||
Expr::TagDeclarator(_) => {}
|
Expr::TagDeclarator(_) => {}
|
||||||
Expr::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value),
|
Expr::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value),
|
||||||
Expr::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
|
Expr::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
|
||||||
|
Expr::IfExpression(_) => {}
|
||||||
Expr::PipeSubstitution(_) => {}
|
Expr::PipeSubstitution(_) => {}
|
||||||
Expr::None(_) => {}
|
Expr::None(_) => {}
|
||||||
}
|
}
|
||||||
@ -603,6 +622,7 @@ impl Expr {
|
|||||||
Expr::ObjectExpression(object_expression) => object_expression.start(),
|
Expr::ObjectExpression(object_expression) => object_expression.start(),
|
||||||
Expr::MemberExpression(member_expression) => member_expression.start(),
|
Expr::MemberExpression(member_expression) => member_expression.start(),
|
||||||
Expr::UnaryExpression(unary_expression) => unary_expression.start(),
|
Expr::UnaryExpression(unary_expression) => unary_expression.start(),
|
||||||
|
Expr::IfExpression(expr) => expr.start(),
|
||||||
Expr::None(none) => none.start,
|
Expr::None(none) => none.start,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -621,6 +641,7 @@ impl Expr {
|
|||||||
Expr::ObjectExpression(object_expression) => object_expression.end(),
|
Expr::ObjectExpression(object_expression) => object_expression.end(),
|
||||||
Expr::MemberExpression(member_expression) => member_expression.end(),
|
Expr::MemberExpression(member_expression) => member_expression.end(),
|
||||||
Expr::UnaryExpression(unary_expression) => unary_expression.end(),
|
Expr::UnaryExpression(unary_expression) => unary_expression.end(),
|
||||||
|
Expr::IfExpression(expr) => expr.end(),
|
||||||
Expr::None(none) => none.end,
|
Expr::None(none) => none.end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -639,6 +660,7 @@ impl Expr {
|
|||||||
Expr::ObjectExpression(object_expression) => object_expression.get_hover_value_for_position(pos, code),
|
Expr::ObjectExpression(object_expression) => object_expression.get_hover_value_for_position(pos, code),
|
||||||
Expr::MemberExpression(member_expression) => member_expression.get_hover_value_for_position(pos, code),
|
Expr::MemberExpression(member_expression) => member_expression.get_hover_value_for_position(pos, code),
|
||||||
Expr::UnaryExpression(unary_expression) => unary_expression.get_hover_value_for_position(pos, code),
|
Expr::UnaryExpression(unary_expression) => unary_expression.get_hover_value_for_position(pos, code),
|
||||||
|
Expr::IfExpression(expr) => expr.get_hover_value_for_position(pos, code),
|
||||||
// TODO: LSP hover information for values/types. https://github.com/KittyCAD/modeling-app/issues/1126
|
// TODO: LSP hover information for values/types. https://github.com/KittyCAD/modeling-app/issues/1126
|
||||||
Expr::None(_) => None,
|
Expr::None(_) => None,
|
||||||
Expr::Literal(_) => None,
|
Expr::Literal(_) => None,
|
||||||
@ -670,11 +692,12 @@ impl Expr {
|
|||||||
member_expression.rename_identifiers(old_name, new_name)
|
member_expression.rename_identifiers(old_name, new_name)
|
||||||
}
|
}
|
||||||
Expr::UnaryExpression(ref mut unary_expression) => unary_expression.rename_identifiers(old_name, new_name),
|
Expr::UnaryExpression(ref mut unary_expression) => unary_expression.rename_identifiers(old_name, new_name),
|
||||||
|
Expr::IfExpression(ref mut expr) => expr.rename_identifiers(old_name, new_name),
|
||||||
Expr::None(_) => {}
|
Expr::None(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the constraint level for a value type.
|
/// Get the constraint level for an expression.
|
||||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||||
match self {
|
match self {
|
||||||
Expr::Literal(literal) => literal.get_constraint_level(),
|
Expr::Literal(literal) => literal.get_constraint_level(),
|
||||||
@ -692,6 +715,7 @@ impl Expr {
|
|||||||
Expr::ObjectExpression(object_expression) => object_expression.get_constraint_level(),
|
Expr::ObjectExpression(object_expression) => object_expression.get_constraint_level(),
|
||||||
Expr::MemberExpression(member_expression) => member_expression.get_constraint_level(),
|
Expr::MemberExpression(member_expression) => member_expression.get_constraint_level(),
|
||||||
Expr::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
|
Expr::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
|
||||||
|
Expr::IfExpression(expr) => expr.get_constraint_level(),
|
||||||
Expr::None(none) => none.get_constraint_level(),
|
Expr::None(none) => none.get_constraint_level(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -720,6 +744,7 @@ pub enum BinaryPart {
|
|||||||
CallExpression(Box<CallExpression>),
|
CallExpression(Box<CallExpression>),
|
||||||
UnaryExpression(Box<UnaryExpression>),
|
UnaryExpression(Box<UnaryExpression>),
|
||||||
MemberExpression(Box<MemberExpression>),
|
MemberExpression(Box<MemberExpression>),
|
||||||
|
IfExpression(Box<IfExpression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BinaryPart> for SourceRange {
|
impl From<BinaryPart> for SourceRange {
|
||||||
@ -743,6 +768,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::CallExpression(ce) => ce.compute_digest(),
|
BinaryPart::CallExpression(ce) => ce.compute_digest(),
|
||||||
BinaryPart::UnaryExpression(ue) => ue.compute_digest(),
|
BinaryPart::UnaryExpression(ue) => ue.compute_digest(),
|
||||||
BinaryPart::MemberExpression(me) => me.compute_digest(),
|
BinaryPart::MemberExpression(me) => me.compute_digest(),
|
||||||
|
BinaryPart::IfExpression(e) => e.compute_digest(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,6 +781,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::CallExpression(call_expression) => call_expression.get_constraint_level(),
|
BinaryPart::CallExpression(call_expression) => call_expression.get_constraint_level(),
|
||||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
|
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
|
||||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_constraint_level(),
|
BinaryPart::MemberExpression(member_expression) => member_expression.get_constraint_level(),
|
||||||
|
BinaryPart::IfExpression(e) => e.get_constraint_level(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,6 +799,7 @@ impl BinaryPart {
|
|||||||
unary_expression.replace_value(source_range, new_value)
|
unary_expression.replace_value(source_range, new_value)
|
||||||
}
|
}
|
||||||
BinaryPart::MemberExpression(_) => {}
|
BinaryPart::MemberExpression(_) => {}
|
||||||
|
BinaryPart::IfExpression(e) => e.replace_value(source_range, new_value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,6 +811,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::CallExpression(call_expression) => call_expression.start(),
|
BinaryPart::CallExpression(call_expression) => call_expression.start(),
|
||||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.start(),
|
BinaryPart::UnaryExpression(unary_expression) => unary_expression.start(),
|
||||||
BinaryPart::MemberExpression(member_expression) => member_expression.start(),
|
BinaryPart::MemberExpression(member_expression) => member_expression.start(),
|
||||||
|
BinaryPart::IfExpression(e) => e.start(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,6 +823,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::CallExpression(call_expression) => call_expression.end(),
|
BinaryPart::CallExpression(call_expression) => call_expression.end(),
|
||||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.end(),
|
BinaryPart::UnaryExpression(unary_expression) => unary_expression.end(),
|
||||||
BinaryPart::MemberExpression(member_expression) => member_expression.end(),
|
BinaryPart::MemberExpression(member_expression) => member_expression.end(),
|
||||||
|
BinaryPart::IfExpression(e) => e.end(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,6 +839,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::CallExpression(call_expression) => call_expression.execute(exec_state, ctx).await,
|
BinaryPart::CallExpression(call_expression) => call_expression.execute(exec_state, ctx).await,
|
||||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
|
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
|
||||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
|
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
|
||||||
|
BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -822,6 +853,7 @@ impl BinaryPart {
|
|||||||
}
|
}
|
||||||
BinaryPart::CallExpression(call_expression) => call_expression.get_hover_value_for_position(pos, code),
|
BinaryPart::CallExpression(call_expression) => call_expression.get_hover_value_for_position(pos, code),
|
||||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_hover_value_for_position(pos, code),
|
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_hover_value_for_position(pos, code),
|
||||||
|
BinaryPart::IfExpression(e) => e.get_hover_value_for_position(pos, code),
|
||||||
BinaryPart::MemberExpression(member_expression) => {
|
BinaryPart::MemberExpression(member_expression) => {
|
||||||
member_expression.get_hover_value_for_position(pos, code)
|
member_expression.get_hover_value_for_position(pos, code)
|
||||||
}
|
}
|
||||||
@ -845,6 +877,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::MemberExpression(ref mut member_expression) => {
|
BinaryPart::MemberExpression(ref mut member_expression) => {
|
||||||
member_expression.rename_identifiers(old_name, new_name)
|
member_expression.rename_identifiers(old_name, new_name)
|
||||||
}
|
}
|
||||||
|
BinaryPart::IfExpression(ref mut if_expression) => if_expression.rename_identifiers(old_name, new_name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1331,7 +1364,7 @@ impl CallExpression {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match exec_result {
|
match exec_result {
|
||||||
Ok(()) => {}
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// We need to override the source ranges so we don't get the embedded kcl
|
// We need to override the source ranges so we don't get the embedded kcl
|
||||||
// function from the stdlib.
|
// function from the stdlib.
|
||||||
@ -3149,6 +3182,7 @@ async fn inner_execute_pipe_body(
|
|||||||
| Expr::ObjectExpression(_)
|
| Expr::ObjectExpression(_)
|
||||||
| Expr::MemberExpression(_)
|
| Expr::MemberExpression(_)
|
||||||
| Expr::UnaryExpression(_)
|
| Expr::UnaryExpression(_)
|
||||||
|
| Expr::IfExpression(_)
|
||||||
| Expr::None(_) => {}
|
| Expr::None(_) => {}
|
||||||
};
|
};
|
||||||
let metadata = Metadata {
|
let metadata = Metadata {
|
||||||
|
215
src/wasm-lib/kcl/src/ast/types/condition.rs
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
use crate::errors::KclError;
|
||||||
|
use crate::executor::BodyType;
|
||||||
|
use crate::executor::ExecState;
|
||||||
|
use crate::executor::ExecutorContext;
|
||||||
|
use crate::executor::KclValue;
|
||||||
|
use crate::executor::Metadata;
|
||||||
|
use crate::executor::SourceRange;
|
||||||
|
use crate::executor::StatementKind;
|
||||||
|
|
||||||
|
use super::compute_digest;
|
||||||
|
use super::impl_value_meta;
|
||||||
|
use super::ConstraintLevel;
|
||||||
|
use super::Hover;
|
||||||
|
use super::{Digest, Expr};
|
||||||
|
use databake::*;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::{Digest as DigestTrait, Sha256};
|
||||||
|
|
||||||
|
// TODO: This should be its own type, similar to Program,
|
||||||
|
// but guaranteed to have an Expression as its final item.
|
||||||
|
// https://github.com/KittyCAD/modeling-app/issues/4015
|
||||||
|
type IfBlock = crate::ast::types::Program;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub struct IfExpression {
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
pub cond: Box<Expr>,
|
||||||
|
pub then_val: Box<IfBlock>,
|
||||||
|
pub else_ifs: Vec<ElseIf>,
|
||||||
|
pub final_else: Box<IfBlock>,
|
||||||
|
|
||||||
|
pub digest: Option<Digest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub struct ElseIf {
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
pub cond: Expr,
|
||||||
|
pub then_val: Box<IfBlock>,
|
||||||
|
|
||||||
|
pub digest: Option<Digest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source code metadata
|
||||||
|
|
||||||
|
impl_value_meta!(IfExpression);
|
||||||
|
impl_value_meta!(ElseIf);
|
||||||
|
|
||||||
|
impl IfExpression {
|
||||||
|
compute_digest!(|slf, hasher| {
|
||||||
|
hasher.update(slf.cond.compute_digest());
|
||||||
|
hasher.update(slf.then_val.compute_digest());
|
||||||
|
for else_if in &mut slf.else_ifs {
|
||||||
|
hasher.update(else_if.compute_digest());
|
||||||
|
}
|
||||||
|
hasher.update(slf.final_else.compute_digest());
|
||||||
|
});
|
||||||
|
fn source_ranges(&self) -> Vec<SourceRange> {
|
||||||
|
vec![SourceRange::from(self)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IfExpression> for Metadata {
|
||||||
|
fn from(value: IfExpression) -> Self {
|
||||||
|
Self {
|
||||||
|
source_range: value.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ElseIf> for Metadata {
|
||||||
|
fn from(value: ElseIf) -> Self {
|
||||||
|
Self {
|
||||||
|
source_range: value.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&IfExpression> for Metadata {
|
||||||
|
fn from(value: &IfExpression) -> Self {
|
||||||
|
Self {
|
||||||
|
source_range: value.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&ElseIf> for Metadata {
|
||||||
|
fn from(value: &ElseIf) -> Self {
|
||||||
|
Self {
|
||||||
|
source_range: value.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElseIf {
|
||||||
|
compute_digest!(|slf, hasher| {
|
||||||
|
hasher.update(slf.cond.compute_digest());
|
||||||
|
hasher.update(slf.then_val.compute_digest());
|
||||||
|
});
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn source_ranges(&self) -> Vec<SourceRange> {
|
||||||
|
vec![SourceRange([self.start, self.end])]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execution
|
||||||
|
|
||||||
|
impl IfExpression {
|
||||||
|
#[async_recursion::async_recursion]
|
||||||
|
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||||
|
// Check the `if` branch.
|
||||||
|
let cond = ctx
|
||||||
|
.execute_expr(&self.cond, exec_state, &Metadata::from(self), StatementKind::Expression)
|
||||||
|
.await?
|
||||||
|
.get_bool()?;
|
||||||
|
if cond {
|
||||||
|
let block_result = ctx.inner_execute(&self.then_val, exec_state, BodyType::Block).await?;
|
||||||
|
// Block must end in an expression, so this has to be Some.
|
||||||
|
// Enforced by the parser.
|
||||||
|
// See https://github.com/KittyCAD/modeling-app/issues/4015
|
||||||
|
return Ok(block_result.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check any `else if` branches.
|
||||||
|
for else_if in &self.else_ifs {
|
||||||
|
let cond = ctx
|
||||||
|
.execute_expr(
|
||||||
|
&else_if.cond,
|
||||||
|
exec_state,
|
||||||
|
&Metadata::from(self),
|
||||||
|
StatementKind::Expression,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.get_bool()?;
|
||||||
|
if cond {
|
||||||
|
let block_result = ctx
|
||||||
|
.inner_execute(&else_if.then_val, exec_state, BodyType::Block)
|
||||||
|
.await?;
|
||||||
|
// Block must end in an expression, so this has to be Some.
|
||||||
|
// Enforced by the parser.
|
||||||
|
// See https://github.com/KittyCAD/modeling-app/issues/4015
|
||||||
|
return Ok(block_result.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the final `else` branch.
|
||||||
|
ctx.inner_execute(&self.final_else, exec_state, BodyType::Block)
|
||||||
|
.await
|
||||||
|
.map(|expr| expr.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDE support and refactors
|
||||||
|
|
||||||
|
impl IfExpression {
|
||||||
|
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||||
|
self.cond
|
||||||
|
.get_hover_value_for_position(pos, code)
|
||||||
|
.or_else(|| self.then_val.get_hover_value_for_position(pos, code))
|
||||||
|
.or_else(|| {
|
||||||
|
self.else_ifs
|
||||||
|
.iter()
|
||||||
|
.find_map(|else_if| else_if.get_hover_value_for_position(pos, code))
|
||||||
|
})
|
||||||
|
.or_else(|| self.final_else.get_hover_value_for_position(pos, code))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rename all identifiers that have the old name to the new given name.
|
||||||
|
pub fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
||||||
|
self.cond.rename_identifiers(old_name, new_name);
|
||||||
|
self.then_val.rename_identifiers(old_name, new_name);
|
||||||
|
for else_if in &mut self.else_ifs {
|
||||||
|
else_if.rename_identifiers(old_name, new_name);
|
||||||
|
}
|
||||||
|
self.final_else.rename_identifiers(old_name, new_name);
|
||||||
|
}
|
||||||
|
/// Get the constraint level.
|
||||||
|
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||||
|
ConstraintLevel::Full {
|
||||||
|
source_ranges: self.source_ranges(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) {
|
||||||
|
self.cond.replace_value(source_range, new_value.clone());
|
||||||
|
for else_if in &mut self.else_ifs {
|
||||||
|
else_if.cond.replace_value(source_range, new_value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElseIf {
|
||||||
|
fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||||
|
self.cond
|
||||||
|
.get_hover_value_for_position(pos, code)
|
||||||
|
.or_else(|| self.then_val.get_hover_value_for_position(pos, code))
|
||||||
|
}
|
||||||
|
/// Rename all identifiers that have the old name to the new given name.
|
||||||
|
fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
||||||
|
self.cond.rename_identifiers(old_name, new_name);
|
||||||
|
self.then_val.rename_identifiers(old_name, new_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linting
|
||||||
|
|
||||||
|
impl IfExpression {}
|
||||||
|
impl ElseIf {}
|
@ -81,6 +81,8 @@ impl StdLibFnArg {
|
|||||||
} else if self.type_ == "TagIdentifier" && self.required {
|
} else if self.type_ == "TagIdentifier" && self.required {
|
||||||
// TODO: actually use the ast to populate this.
|
// TODO: actually use the ast to populate this.
|
||||||
return Ok(Some((index, format!("${{{}:{}}}", index, "myTag"))));
|
return Ok(Some((index, format!("${{{}:{}}}", index, "myTag"))));
|
||||||
|
} else if self.type_ == "[KclValue]" && self.required {
|
||||||
|
return Ok(Some((index, format!("${{{}:{}}}", index, "[0..9]"))));
|
||||||
}
|
}
|
||||||
get_autocomplete_snippet_from_schema(&self.schema.schema.clone().into(), index)
|
get_autocomplete_snippet_from_schema(&self.schema.schema.clone().into(), index)
|
||||||
}
|
}
|
||||||
@ -903,6 +905,13 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_autocomplete_snippet_map() {
|
||||||
|
let map_fn: Box<dyn StdLibFn> = Box::new(crate::std::array::Map);
|
||||||
|
let snippet = map_fn.to_autocomplete_snippet().unwrap();
|
||||||
|
assert_eq!(snippet, r#"map(${0:[0..9]})${}"#);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_autocomplete_snippet_pattern_linear_2d() {
|
fn get_autocomplete_snippet_pattern_linear_2d() {
|
||||||
let pattern_fn: Box<dyn StdLibFn> = Box::new(crate::std::patterns::PatternLinear2D);
|
let pattern_fn: Box<dyn StdLibFn> = Box::new(crate::std::patterns::PatternLinear2D);
|
||||||
@ -916,4 +925,18 @@ mod tests {
|
|||||||
}, ${4:%})${}"#
|
}, ${4:%})${}"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We want to test the snippets we compile at lsp start.
|
||||||
|
#[test]
|
||||||
|
fn get_all_stdlib_autocomplete_snippets() {
|
||||||
|
let stdlib = crate::std::StdLib::new();
|
||||||
|
crate::lsp::kcl::get_completions_from_stdlib(&stdlib).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to test the signatures we compile at lsp start.
|
||||||
|
#[test]
|
||||||
|
fn get_all_stdlib_signatures() {
|
||||||
|
let stdlib = crate::std::StdLib::new();
|
||||||
|
crate::lsp::kcl::get_signatures_from_stdlib(&stdlib).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -776,6 +776,25 @@ impl From<KclValue> for Vec<SourceRange> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&KclValue> for Vec<SourceRange> {
|
||||||
|
fn from(item: &KclValue) -> Self {
|
||||||
|
match item {
|
||||||
|
KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(),
|
||||||
|
KclValue::TagDeclarator(ref t) => vec![t.into()],
|
||||||
|
KclValue::TagIdentifier(t) => t.meta.iter().map(|m| m.source_range).collect(),
|
||||||
|
KclValue::Solid(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||||
|
KclValue::Solids { value } => value
|
||||||
|
.iter()
|
||||||
|
.flat_map(|eg| eg.meta.iter().map(|m| m.source_range))
|
||||||
|
.collect(),
|
||||||
|
KclValue::ImportedGeometry(i) => i.meta.iter().map(|m| m.source_range).collect(),
|
||||||
|
KclValue::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
||||||
|
KclValue::Plane(p) => p.meta.iter().map(|m| m.source_range).collect(),
|
||||||
|
KclValue::Face(f) => f.meta.iter().map(|m| m.source_range).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl KclValue {
|
impl KclValue {
|
||||||
pub fn get_json_value(&self) -> Result<serde_json::Value, KclError> {
|
pub fn get_json_value(&self) -> Result<serde_json::Value, KclError> {
|
||||||
if let KclValue::UserVal(user_val) = self {
|
if let KclValue::UserVal(user_val) = self {
|
||||||
@ -906,6 +925,23 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this KCL value is a bool, retrieve it.
|
||||||
|
pub fn get_bool(&self) -> Result<bool, KclError> {
|
||||||
|
let Self::UserVal(uv) = self else {
|
||||||
|
return Err(KclError::Type(KclErrorDetails {
|
||||||
|
source_ranges: self.into(),
|
||||||
|
message: format!("Expected bool, found {}", self.human_friendly_type()),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
let JValue::Bool(b) = uv.value else {
|
||||||
|
return Err(KclError::Type(KclErrorDetails {
|
||||||
|
source_ranges: self.into(),
|
||||||
|
message: format!("Expected bool, found {}", human_friendly_type(&uv.value)),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
Ok(b)
|
||||||
|
}
|
||||||
|
|
||||||
/// If this memory item is a function, call it with the given arguments, return its val as Ok.
|
/// If this memory item is a function, call it with the given arguments, return its val as Ok.
|
||||||
/// If it's not a function, return Err.
|
/// If it's not a function, return Err.
|
||||||
pub async fn call_fn(
|
pub async fn call_fn(
|
||||||
@ -1613,6 +1649,22 @@ impl ExtrudeSurface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of ExecutorContext being used
|
||||||
|
#[derive(PartialEq, Debug, Default, Clone)]
|
||||||
|
pub enum ContextType {
|
||||||
|
/// Live engine connection
|
||||||
|
#[default]
|
||||||
|
Live,
|
||||||
|
|
||||||
|
/// Completely mocked connection
|
||||||
|
/// Mock mode is only for the modeling app when they just want to mock engine calls and not
|
||||||
|
/// actually make them.
|
||||||
|
Mock,
|
||||||
|
|
||||||
|
/// Handled by some other interpreter/conversion system
|
||||||
|
MockCustomForwarded,
|
||||||
|
}
|
||||||
|
|
||||||
/// The executor context.
|
/// The executor context.
|
||||||
/// Cloning will return another handle to the same engine connection/session,
|
/// Cloning will return another handle to the same engine connection/session,
|
||||||
/// as this uses `Arc` under the hood.
|
/// as this uses `Arc` under the hood.
|
||||||
@ -1622,9 +1674,7 @@ pub struct ExecutorContext {
|
|||||||
pub fs: Arc<FileManager>,
|
pub fs: Arc<FileManager>,
|
||||||
pub stdlib: Arc<StdLib>,
|
pub stdlib: Arc<StdLib>,
|
||||||
pub settings: ExecutorSettings,
|
pub settings: ExecutorSettings,
|
||||||
/// Mock mode is only for the modeling app when they just want to mock engine calls and not
|
pub context_type: ContextType,
|
||||||
/// actually make them.
|
|
||||||
pub is_mock: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The executor settings.
|
/// The executor settings.
|
||||||
@ -1734,10 +1784,14 @@ impl ExecutorContext {
|
|||||||
fs: Arc::new(FileManager::new()),
|
fs: Arc::new(FileManager::new()),
|
||||||
stdlib: Arc::new(StdLib::new()),
|
stdlib: Arc::new(StdLib::new()),
|
||||||
settings,
|
settings,
|
||||||
is_mock: false,
|
context_type: ContextType::Live,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_mock(&self) -> bool {
|
||||||
|
self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded
|
||||||
|
}
|
||||||
|
|
||||||
/// For executing unit tests.
|
/// For executing unit tests.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub async fn new_for_unit_test(units: UnitLength, engine_addr: Option<String>) -> Result<Self> {
|
pub async fn new_for_unit_test(units: UnitLength, engine_addr: Option<String>) -> Result<Self> {
|
||||||
@ -1844,20 +1898,22 @@ impl ExecutorContext {
|
|||||||
program: &crate::ast::types::Program,
|
program: &crate::ast::types::Program,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
body_type: BodyType,
|
body_type: BodyType,
|
||||||
) -> Result<(), KclError> {
|
) -> Result<Option<KclValue>, KclError> {
|
||||||
|
let mut last_expr = None;
|
||||||
// Iterate over the body of the program.
|
// Iterate over the body of the program.
|
||||||
for statement in &program.body {
|
for statement in &program.body {
|
||||||
match statement {
|
match statement {
|
||||||
BodyItem::ExpressionStatement(expression_statement) => {
|
BodyItem::ExpressionStatement(expression_statement) => {
|
||||||
let metadata = Metadata::from(expression_statement);
|
let metadata = Metadata::from(expression_statement);
|
||||||
// Discard return value.
|
last_expr = Some(
|
||||||
self.execute_expr(
|
self.execute_expr(
|
||||||
&expression_statement.expression,
|
&expression_statement.expression,
|
||||||
exec_state,
|
exec_state,
|
||||||
&metadata,
|
&metadata,
|
||||||
StatementKind::Expression,
|
StatementKind::Expression,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
BodyItem::VariableDeclaration(variable_declaration) => {
|
BodyItem::VariableDeclaration(variable_declaration) => {
|
||||||
for declaration in &variable_declaration.declarations {
|
for declaration in &variable_declaration.declarations {
|
||||||
@ -1875,6 +1931,7 @@ impl ExecutorContext {
|
|||||||
.await?;
|
.await?;
|
||||||
exec_state.memory.add(&var_name, memory_item, source_range)?;
|
exec_state.memory.add(&var_name, memory_item, source_range)?;
|
||||||
}
|
}
|
||||||
|
last_expr = None;
|
||||||
}
|
}
|
||||||
BodyItem::ReturnStatement(return_statement) => {
|
BodyItem::ReturnStatement(return_statement) => {
|
||||||
let metadata = Metadata::from(return_statement);
|
let metadata = Metadata::from(return_statement);
|
||||||
@ -1887,6 +1944,7 @@ impl ExecutorContext {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
exec_state.memory.return_ = Some(value);
|
exec_state.memory.return_ = Some(value);
|
||||||
|
last_expr = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1903,7 +1961,7 @@ impl ExecutorContext {
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(last_expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute_expr<'a>(
|
pub async fn execute_expr<'a>(
|
||||||
@ -1960,6 +2018,7 @@ impl ExecutorContext {
|
|||||||
Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
|
Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
|
||||||
Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state)?,
|
Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state)?,
|
||||||
Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
|
Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
|
||||||
|
Expr::IfExpression(expr) => expr.get_result(exec_state, self).await?,
|
||||||
};
|
};
|
||||||
Ok(item)
|
Ok(item)
|
||||||
}
|
}
|
||||||
@ -2088,7 +2147,7 @@ pub(crate) async fn call_user_defined_function(
|
|||||||
(result, fn_memory)
|
(result, fn_memory)
|
||||||
};
|
};
|
||||||
|
|
||||||
result.map(|()| fn_memory.return_)
|
result.map(|_| fn_memory.return_)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum StatementKind<'a> {
|
pub enum StatementKind<'a> {
|
||||||
@ -2114,7 +2173,7 @@ mod tests {
|
|||||||
fs: Arc::new(crate::fs::FileManager::new()),
|
fs: Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: Arc::new(crate::std::StdLib::new()),
|
stdlib: Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: ContextType::Mock,
|
||||||
};
|
};
|
||||||
let exec_state = ctx.run(&program, None).await?;
|
let exec_state = ctx.run(&program, None).await?;
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@ use winnow::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
ArrayExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression, CommentStyle, Expr,
|
ArrayExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression, CommentStyle, ElseIf,
|
||||||
ExpressionStatement, FnArgPrimitive, FnArgType, FunctionExpression, Identifier, Literal, LiteralIdentifier,
|
Expr, ExpressionStatement, FnArgPrimitive, FnArgType, FunctionExpression, Identifier, IfExpression, Literal,
|
||||||
LiteralValue, MemberExpression, MemberObject, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression,
|
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, NonCodeMeta, NonCodeNode, NonCodeValue,
|
||||||
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, TagDeclarator,
|
ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement,
|
||||||
UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
TagDeclarator, UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||||
},
|
},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::SourceRange,
|
executor::SourceRange,
|
||||||
@ -359,6 +359,7 @@ fn operand(i: TokenSlice) -> PResult<BinaryPart> {
|
|||||||
Expr::BinaryExpression(x) => BinaryPart::BinaryExpression(x),
|
Expr::BinaryExpression(x) => BinaryPart::BinaryExpression(x),
|
||||||
Expr::CallExpression(x) => BinaryPart::CallExpression(x),
|
Expr::CallExpression(x) => BinaryPart::CallExpression(x),
|
||||||
Expr::MemberExpression(x) => BinaryPart::MemberExpression(x),
|
Expr::MemberExpression(x) => BinaryPart::MemberExpression(x),
|
||||||
|
Expr::IfExpression(x) => BinaryPart::IfExpression(x),
|
||||||
};
|
};
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
})
|
})
|
||||||
@ -670,6 +671,119 @@ fn pipe_sub(i: TokenSlice) -> PResult<PipeSubstitution> {
|
|||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn else_if(i: TokenSlice) -> PResult<ElseIf> {
|
||||||
|
let start = any
|
||||||
|
.try_map(|token: Token| {
|
||||||
|
if matches!(token.token_type, TokenType::Keyword) && token.value == "else" {
|
||||||
|
Ok(token.start)
|
||||||
|
} else {
|
||||||
|
Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: token.as_source_ranges(),
|
||||||
|
message: format!("{} is not 'else'", token.value.as_str()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context(expected("the 'else' keyword"))
|
||||||
|
.parse_next(i)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let _if = any
|
||||||
|
.try_map(|token: Token| {
|
||||||
|
if matches!(token.token_type, TokenType::Keyword) && token.value == "if" {
|
||||||
|
Ok(token.start)
|
||||||
|
} else {
|
||||||
|
Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: token.as_source_ranges(),
|
||||||
|
message: format!("{} is not 'if'", token.value.as_str()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context(expected("the 'if' keyword"))
|
||||||
|
.parse_next(i)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let cond = expression(i)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let _ = open_brace(i)?;
|
||||||
|
let then_val = program
|
||||||
|
.verify(|block| block.ends_with_expr())
|
||||||
|
.parse_next(i)
|
||||||
|
.map(Box::new)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let end = close_brace(i)?.end;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
Ok(ElseIf {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
cond,
|
||||||
|
then_val,
|
||||||
|
digest: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_expr(i: TokenSlice) -> PResult<IfExpression> {
|
||||||
|
let start = any
|
||||||
|
.try_map(|token: Token| {
|
||||||
|
if matches!(token.token_type, TokenType::Keyword) && token.value == "if" {
|
||||||
|
Ok(token.start)
|
||||||
|
} else {
|
||||||
|
Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: token.as_source_ranges(),
|
||||||
|
message: format!("{} is not 'if'", token.value.as_str()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context(expected("the 'if' keyword"))
|
||||||
|
.parse_next(i)?;
|
||||||
|
let _ = whitespace(i)?;
|
||||||
|
let cond = expression(i).map(Box::new)?;
|
||||||
|
let _ = whitespace(i)?;
|
||||||
|
let _ = open_brace(i)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let then_val = program
|
||||||
|
.verify(|block| block.ends_with_expr())
|
||||||
|
.parse_next(i)
|
||||||
|
.map_err(|e| e.cut())
|
||||||
|
.map(Box::new)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let _ = close_brace(i)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let else_ifs = repeat(0.., else_if).parse_next(i)?;
|
||||||
|
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let _ = any
|
||||||
|
.try_map(|token: Token| {
|
||||||
|
if matches!(token.token_type, TokenType::Keyword) && token.value == "else" {
|
||||||
|
Ok(token.start)
|
||||||
|
} else {
|
||||||
|
Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: token.as_source_ranges(),
|
||||||
|
message: format!("{} is not 'else'", token.value.as_str()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context(expected("the 'else' keyword"))
|
||||||
|
.parse_next(i)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let _ = open_brace(i)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
|
||||||
|
let final_else = program
|
||||||
|
.verify(|block| block.ends_with_expr())
|
||||||
|
.parse_next(i)
|
||||||
|
.map_err(|e| e.cut())
|
||||||
|
.map(Box::new)?;
|
||||||
|
ignore_whitespace(i);
|
||||||
|
let end = close_brace(i)?.end;
|
||||||
|
Ok(IfExpression {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
cond,
|
||||||
|
then_val,
|
||||||
|
else_ifs,
|
||||||
|
final_else,
|
||||||
|
digest: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Looks like
|
// Looks like
|
||||||
// (arg0, arg1) => {
|
// (arg0, arg1) => {
|
||||||
// const x = arg0 + arg1;
|
// const x = arg0 + arg1;
|
||||||
@ -1069,6 +1183,7 @@ fn expr_allowed_in_pipe_expr(i: TokenSlice) -> PResult<Expr> {
|
|||||||
object.map(Box::new).map(Expr::ObjectExpression),
|
object.map(Box::new).map(Expr::ObjectExpression),
|
||||||
pipe_sub.map(Box::new).map(Expr::PipeSubstitution),
|
pipe_sub.map(Box::new).map(Expr::PipeSubstitution),
|
||||||
function_expression.map(Box::new).map(Expr::FunctionExpression),
|
function_expression.map(Box::new).map(Expr::FunctionExpression),
|
||||||
|
if_expr.map(Box::new).map(Expr::IfExpression),
|
||||||
unnecessarily_bracketed,
|
unnecessarily_bracketed,
|
||||||
))
|
))
|
||||||
.context(expected("a KCL expression (but not a pipe expression)"))
|
.context(expected("a KCL expression (but not a pipe expression)"))
|
||||||
@ -3147,6 +3262,42 @@ e
|
|||||||
let _arr = array_elem_by_elem(&mut sl).unwrap();
|
let _arr = array_elem_by_elem(&mut sl).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_if_else() {
|
||||||
|
let some_program_string = "if true {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
4
|
||||||
|
}";
|
||||||
|
let tokens = crate::token::lexer(some_program_string).unwrap();
|
||||||
|
let mut sl: &[Token] = &tokens;
|
||||||
|
let _res = if_expr(&mut sl).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_else_if() {
|
||||||
|
let some_program_string = "else if true {
|
||||||
|
4
|
||||||
|
}";
|
||||||
|
let tokens = crate::token::lexer(some_program_string).unwrap();
|
||||||
|
let mut sl: &[Token] = &tokens;
|
||||||
|
let _res = else_if(&mut sl).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_if_else_if() {
|
||||||
|
let some_program_string = "if true {
|
||||||
|
3
|
||||||
|
} else if true {
|
||||||
|
4
|
||||||
|
} else {
|
||||||
|
5
|
||||||
|
}";
|
||||||
|
let tokens = crate::token::lexer(some_program_string).unwrap();
|
||||||
|
let mut sl: &[Token] = &tokens;
|
||||||
|
let _res = if_expr(&mut sl).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keyword_ok_in_fn_args_return() {
|
fn test_keyword_ok_in_fn_args_return() {
|
||||||
let some_program_string = r#"fn thing = (param) => {
|
let some_program_string = r#"fn thing = (param) => {
|
||||||
@ -3511,6 +3662,24 @@ const sketch001 = startSketchOn('XY')
|
|||||||
const my14 = 4 ^ 2 - 3 ^ 2 * 2
|
const my14 = 4 ^ 2 - 3 ^ 2 * 2
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
|
snapshot_test!(
|
||||||
|
bc,
|
||||||
|
r#"const x = if true {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
4
|
||||||
|
}"#
|
||||||
|
);
|
||||||
|
snapshot_test!(
|
||||||
|
bd,
|
||||||
|
r#"const x = if true {
|
||||||
|
3
|
||||||
|
} else if func(radius) {
|
||||||
|
4
|
||||||
|
} else {
|
||||||
|
5
|
||||||
|
}"#
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/parser/parser_impl.rs
|
||||||
|
expression: actual
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"start": 0,
|
||||||
|
"end": 74,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"start": 0,
|
||||||
|
"end": 74,
|
||||||
|
"declarations": [
|
||||||
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
|
"start": 6,
|
||||||
|
"end": 74,
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 6,
|
||||||
|
"end": 7,
|
||||||
|
"name": "x",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"type": "IfExpression",
|
||||||
|
"type": "IfExpression",
|
||||||
|
"start": 10,
|
||||||
|
"end": 74,
|
||||||
|
"cond": {
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"start": 13,
|
||||||
|
"end": 17,
|
||||||
|
"value": true,
|
||||||
|
"raw": "true",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"then_val": {
|
||||||
|
"start": 32,
|
||||||
|
"end": 42,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"start": 32,
|
||||||
|
"end": 33,
|
||||||
|
"expression": {
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"start": 32,
|
||||||
|
"end": 33,
|
||||||
|
"value": 3,
|
||||||
|
"raw": "3",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {},
|
||||||
|
"start": [],
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"else_ifs": [],
|
||||||
|
"final_else": {
|
||||||
|
"start": 63,
|
||||||
|
"end": 73,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"start": 63,
|
||||||
|
"end": 64,
|
||||||
|
"expression": {
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"start": 63,
|
||||||
|
"end": 64,
|
||||||
|
"value": 4,
|
||||||
|
"raw": "4",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {},
|
||||||
|
"start": [],
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"kind": "const",
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {},
|
||||||
|
"start": [],
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/parser/parser_impl.rs
|
||||||
|
expression: actual
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"start": 0,
|
||||||
|
"end": 121,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"start": 0,
|
||||||
|
"end": 121,
|
||||||
|
"declarations": [
|
||||||
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
|
"start": 6,
|
||||||
|
"end": 121,
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 6,
|
||||||
|
"end": 7,
|
||||||
|
"name": "x",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"type": "IfExpression",
|
||||||
|
"type": "IfExpression",
|
||||||
|
"start": 10,
|
||||||
|
"end": 121,
|
||||||
|
"cond": {
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"start": 13,
|
||||||
|
"end": 17,
|
||||||
|
"value": true,
|
||||||
|
"raw": "true",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"then_val": {
|
||||||
|
"start": 32,
|
||||||
|
"end": 42,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"start": 32,
|
||||||
|
"end": 33,
|
||||||
|
"expression": {
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"start": 32,
|
||||||
|
"end": 33,
|
||||||
|
"value": 3,
|
||||||
|
"raw": "3",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {},
|
||||||
|
"start": [],
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"else_ifs": [
|
||||||
|
{
|
||||||
|
"type": "ElseIf",
|
||||||
|
"start": 44,
|
||||||
|
"end": 90,
|
||||||
|
"cond": {
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression",
|
||||||
|
"start": 52,
|
||||||
|
"end": 64,
|
||||||
|
"callee": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 52,
|
||||||
|
"end": 56,
|
||||||
|
"name": "func",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 57,
|
||||||
|
"end": 63,
|
||||||
|
"name": "radius",
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optional": false,
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"then_val": {
|
||||||
|
"start": 65,
|
||||||
|
"end": 89,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"start": 79,
|
||||||
|
"end": 80,
|
||||||
|
"expression": {
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"start": 79,
|
||||||
|
"end": 80,
|
||||||
|
"value": 4,
|
||||||
|
"raw": "4",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {},
|
||||||
|
"start": [],
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"final_else": {
|
||||||
|
"start": 110,
|
||||||
|
"end": 120,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"start": 110,
|
||||||
|
"end": 111,
|
||||||
|
"expression": {
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"start": 110,
|
||||||
|
"end": 111,
|
||||||
|
"value": 5,
|
||||||
|
"raw": "5",
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {},
|
||||||
|
"start": [],
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"kind": "const",
|
||||||
|
"digest": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {},
|
||||||
|
"start": [],
|
||||||
|
"digest": null
|
||||||
|
},
|
||||||
|
"digest": null
|
||||||
|
}
|
@ -4,13 +4,14 @@ use anyhow::Result;
|
|||||||
use kcmc::{websocket::OkWebSocketResponseData, ModelingCmd};
|
use kcmc::{websocket::OkWebSocketResponseData, ModelingCmd};
|
||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
use serde_json::Value as JValue;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{parse_json_number_as_f64, TagDeclarator},
|
ast::types::{parse_json_number_as_f64, TagDeclarator},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{
|
executor::{
|
||||||
ExecState, ExecutorContext, ExtrudeSurface, KclValue, Metadata, Sketch, SketchSet, SketchSurface, Solid,
|
ExecState, ExecutorContext, ExtrudeSurface, KclValue, Metadata, Sketch, SketchSet, SketchSurface, Solid,
|
||||||
SolidSet, SourceRange, TagIdentifier,
|
SolidSet, SourceRange, TagIdentifier, UserVal,
|
||||||
},
|
},
|
||||||
std::{shapes::SketchOrSurface, sketch::FaceTag, FnAsArg},
|
std::{shapes::SketchOrSurface, sketch::FaceTag, FnAsArg},
|
||||||
};
|
};
|
||||||
@ -43,7 +44,7 @@ impl Args {
|
|||||||
fs: Arc::new(crate::fs::FileManager::new()),
|
fs: Arc::new(crate::fs::FileManager::new()),
|
||||||
stdlib: Arc::new(crate::std::StdLib::new()),
|
stdlib: Arc::new(crate::std::StdLib::new()),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
is_mock: true,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -497,18 +498,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromArgs<'a> for KclValue {
|
|
||||||
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
|
|
||||||
let Some(v) = args.args.get(i) else {
|
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
|
||||||
message: format!("Argument at index {i} was missing",),
|
|
||||||
source_ranges: vec![args.source_range],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
Ok(v.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> FromArgs<'a> for Option<T>
|
impl<'a, T> FromArgs<'a> for Option<T>
|
||||||
where
|
where
|
||||||
T: FromKclValue<'a> + Sized,
|
T: FromKclValue<'a> + Sized,
|
||||||
@ -587,6 +576,20 @@ impl<'a> FromKclValue<'a> for i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> FromKclValue<'a> for UserVal {
|
||||||
|
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
|
||||||
|
arg.as_user_val().map(|x| x.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromKclValue<'a> for Vec<JValue> {
|
||||||
|
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
|
||||||
|
arg.as_user_val()
|
||||||
|
.and_then(|uv| uv.value.as_array())
|
||||||
|
.map(ToOwned::to_owned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for TagDeclarator {
|
impl<'a> FromKclValue<'a> for TagDeclarator {
|
||||||
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
|
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
|
||||||
arg.get_tag_declarator().ok()
|
arg.get_tag_declarator().ok()
|
||||||
@ -599,6 +602,12 @@ impl<'a> FromKclValue<'a> for TagIdentifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> FromKclValue<'a> for KclValue {
|
||||||
|
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
|
||||||
|
Some(arg.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_from_arg_via_json {
|
macro_rules! impl_from_arg_via_json {
|
||||||
($typ:path) => {
|
($typ:path) => {
|
||||||
impl<'a> FromKclValue<'a> for $typ {
|
impl<'a> FromKclValue<'a> for $typ {
|
||||||
@ -609,6 +618,15 @@ macro_rules! impl_from_arg_via_json {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T> FromKclValue<'a> for Vec<T>
|
||||||
|
where
|
||||||
|
T: serde::de::DeserializeOwned + FromKclValue<'a>,
|
||||||
|
{
|
||||||
|
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
|
||||||
|
from_user_val(arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_from_arg_for_array {
|
macro_rules! impl_from_arg_for_array {
|
||||||
($n:literal) => {
|
($n:literal) => {
|
||||||
impl<'a, T> FromKclValue<'a> for [T; $n]
|
impl<'a, T> FromKclValue<'a> for [T; $n]
|
||||||
@ -722,23 +740,3 @@ impl<'a> FromKclValue<'a> for SketchSurface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for Vec<Sketch> {
|
|
||||||
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
|
|
||||||
let KclValue::UserVal(uv) = arg else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
uv.get::<Vec<Sketch>>().map(|x| x.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for Vec<u64> {
|
|
||||||
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
|
|
||||||
let KclValue::UserVal(uv) = arg else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
uv.get::<Vec<u64>>().map(|x| x.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use derive_docs::stdlib;
|
use derive_docs::stdlib;
|
||||||
|
use serde_json::Value as JValue;
|
||||||
|
|
||||||
use super::{args::FromArgs, Args, FnAsArg};
|
use super::{args::FromArgs, Args, FnAsArg};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -7,8 +8,96 @@ use crate::{
|
|||||||
function_param::FunctionParam,
|
function_param::FunctionParam,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Apply a function to each element of an array.
|
||||||
|
pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
|
let (array, f): (Vec<JValue>, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
||||||
|
let array: Vec<KclValue> = array
|
||||||
|
.into_iter()
|
||||||
|
.map(|jval| {
|
||||||
|
KclValue::UserVal(UserVal {
|
||||||
|
value: jval,
|
||||||
|
meta: vec![args.source_range.into()],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let map_fn = FunctionParam {
|
||||||
|
inner: f.func,
|
||||||
|
fn_expr: f.expr,
|
||||||
|
meta: vec![args.source_range.into()],
|
||||||
|
ctx: args.ctx.clone(),
|
||||||
|
memory: *f.memory,
|
||||||
|
};
|
||||||
|
let new_array = inner_map(array, map_fn, exec_state, &args).await?;
|
||||||
|
let uv = UserVal::new(vec![args.source_range.into()], new_array);
|
||||||
|
Ok(KclValue::UserVal(uv))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a function to every element of a list.
|
||||||
|
///
|
||||||
|
/// Given a list like `[a, b, c]`, and a function like `f`, returns
|
||||||
|
/// `[f(a), f(b), f(c)]`
|
||||||
|
/// ```no_run
|
||||||
|
/// const r = 10 // radius
|
||||||
|
/// fn drawCircle = (id) => {
|
||||||
|
/// return startSketchOn("XY")
|
||||||
|
/// |> circle({ center: [id * 2 * r, 0], radius: r}, %)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Call `drawCircle`, passing in each element of the array.
|
||||||
|
/// // The outputs from each `drawCircle` form a new array,
|
||||||
|
/// // which is the return value from `map`.
|
||||||
|
/// const circles = map(
|
||||||
|
/// [1..3],
|
||||||
|
/// drawCircle
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
/// ```no_run
|
||||||
|
/// const r = 10 // radius
|
||||||
|
/// // Call `map`, using an anonymous function instead of a named one.
|
||||||
|
/// const circles = map(
|
||||||
|
/// [1..3],
|
||||||
|
/// (id) => {
|
||||||
|
/// return startSketchOn("XY")
|
||||||
|
/// |> circle({ center: [id * 2 * r, 0], radius: r}, %)
|
||||||
|
/// }
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
#[stdlib {
|
||||||
|
name = "map",
|
||||||
|
}]
|
||||||
|
async fn inner_map<'a>(
|
||||||
|
array: Vec<KclValue>,
|
||||||
|
map_fn: FunctionParam<'a>,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
|
args: &'a Args,
|
||||||
|
) -> Result<Vec<KclValue>, KclError> {
|
||||||
|
let mut new_array = Vec::with_capacity(array.len());
|
||||||
|
for elem in array {
|
||||||
|
let new_elem = call_map_closure(elem, &map_fn, args.source_range, exec_state).await?;
|
||||||
|
new_array.push(new_elem);
|
||||||
|
}
|
||||||
|
Ok(new_array)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn call_map_closure<'a>(
|
||||||
|
input: KclValue,
|
||||||
|
map_fn: &FunctionParam<'a>,
|
||||||
|
source_range: SourceRange,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
|
) -> Result<KclValue, KclError> {
|
||||||
|
let output = map_fn.call(exec_state, vec![input]).await?;
|
||||||
|
let source_ranges = vec![source_range];
|
||||||
|
let output = output.ok_or_else(|| {
|
||||||
|
KclError::Semantic(KclErrorDetails {
|
||||||
|
message: "Map function must return a value".to_string(),
|
||||||
|
source_ranges,
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
/// For each item in an array, update a value.
|
/// For each item in an array, update a value.
|
||||||
pub async fn array_reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (array, start, f): (Vec<u64>, Sketch, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
let (array, start, f): (Vec<u64>, Sketch, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
||||||
let reduce_fn = FunctionParam {
|
let reduce_fn = FunctionParam {
|
||||||
inner: f.func,
|
inner: f.func,
|
||||||
@ -17,7 +106,7 @@ pub async fn array_reduce(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
|||||||
ctx: args.ctx.clone(),
|
ctx: args.ctx.clone(),
|
||||||
memory: *f.memory,
|
memory: *f.memory,
|
||||||
};
|
};
|
||||||
inner_array_reduce(array, start, reduce_fn, exec_state, &args)
|
inner_reduce(array, start, reduce_fn, exec_state, &args)
|
||||||
.await
|
.await
|
||||||
.map(|sg| KclValue::UserVal(UserVal::new(sg.meta.clone(), sg)))
|
.map(|sg| KclValue::UserVal(UserVal::new(sg.meta.clone(), sg)))
|
||||||
}
|
}
|
||||||
@ -28,7 +117,7 @@ pub async fn array_reduce(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
|||||||
/// fn decagon = (radius) => {
|
/// fn decagon = (radius) => {
|
||||||
/// let step = (1/10) * tau()
|
/// let step = (1/10) * tau()
|
||||||
/// let sketch001 = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
/// let sketch001 = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
||||||
/// return arrayReduce([1..10], sketch001, (i, sg) => {
|
/// return reduce([1..10], sketch001, (i, sg) => {
|
||||||
/// let x = cos(step * i) * radius
|
/// let x = cos(step * i) * radius
|
||||||
/// let y = sin(step * i) * radius
|
/// let y = sin(step * i) * radius
|
||||||
/// return lineTo([x, y], sg)
|
/// return lineTo([x, y], sg)
|
||||||
@ -37,9 +126,9 @@ pub async fn array_reduce(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
|||||||
/// decagon(5.0) |> close(%)
|
/// decagon(5.0) |> close(%)
|
||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "arrayReduce",
|
name = "reduce",
|
||||||
}]
|
}]
|
||||||
async fn inner_array_reduce<'a>(
|
async fn inner_reduce<'a>(
|
||||||
array: Vec<u64>,
|
array: Vec<u64>,
|
||||||
start: Sketch,
|
start: Sketch,
|
||||||
reduce_fn: FunctionParam<'a>,
|
reduce_fn: FunctionParam<'a>,
|
||||||
|
@ -247,7 +247,7 @@ pub(crate) async fn do_post_extrude(sketch: Sketch, length: f64, args: Args) ->
|
|||||||
Some(extrude_surface)
|
Some(extrude_surface)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if args.ctx.is_mock {
|
} else if args.ctx.is_mock() {
|
||||||
// Only pre-populate the extrude surface if we are in mock mode.
|
// Only pre-populate the extrude surface if we are in mock mode.
|
||||||
|
|
||||||
let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::executor::ExtrudePlane {
|
let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::executor::ExtrudePlane {
|
||||||
@ -296,7 +296,7 @@ fn analyze_faces(args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
|
|||||||
sides: HashMap::with_capacity(face_infos.len()),
|
sides: HashMap::with_capacity(face_infos.len()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if args.ctx.is_mock {
|
if args.ctx.is_mock() {
|
||||||
// Create fake IDs for start and end caps, to make extrudes mock-execute safe
|
// Create fake IDs for start and end caps, to make extrudes mock-execute safe
|
||||||
faces.start_cap_id = Some(Uuid::new_v4());
|
faces.start_cap_id = Some(Uuid::new_v4());
|
||||||
faces.end_cap_id = Some(Uuid::new_v4());
|
faces.end_cap_id = Some(Uuid::new_v4());
|
||||||
|
@ -228,7 +228,7 @@ pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
name = "getOppositeEdge",
|
name = "getOppositeEdge",
|
||||||
}]
|
}]
|
||||||
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
|
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
|
||||||
if args.ctx.is_mock {
|
if args.ctx.is_mock() {
|
||||||
return Ok(Uuid::new_v4());
|
return Ok(Uuid::new_v4());
|
||||||
}
|
}
|
||||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||||
@ -309,7 +309,7 @@ async fn inner_get_next_adjacent_edge(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Uuid, KclError> {
|
) -> Result<Uuid, KclError> {
|
||||||
if args.ctx.is_mock {
|
if args.ctx.is_mock() {
|
||||||
return Ok(Uuid::new_v4());
|
return Ok(Uuid::new_v4());
|
||||||
}
|
}
|
||||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||||
@ -398,7 +398,7 @@ async fn inner_get_previous_adjacent_edge(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Uuid, KclError> {
|
) -> Result<Uuid, KclError> {
|
||||||
if args.ctx.is_mock {
|
if args.ctx.is_mock() {
|
||||||
return Ok(Uuid::new_v4());
|
return Ok(Uuid::new_v4());
|
||||||
}
|
}
|
||||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||||
|
@ -284,7 +284,7 @@ async fn inner_import(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.ctx.is_mock {
|
if args.ctx.is_mock() {
|
||||||
return Ok(ImportedGeometry {
|
return Ok(ImportedGeometry {
|
||||||
id: uuid::Uuid::new_v4(),
|
id: uuid::Uuid::new_v4(),
|
||||||
value: import_files.iter().map(|f| f.path.to_string()).collect(),
|
value: import_files.iter().map(|f| f.path.to_string()).collect(),
|
||||||
|