Compare commits
1 Commits
kcl-0.2.28
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
078ffa02b0 |
17
.github/workflows/build-apps.yml
vendored
@ -383,13 +383,12 @@ jobs:
|
||||
parent: false
|
||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||
|
||||
- name: Tag nightly commit
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
uses: actions/github-script@v7
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: ${{ env.IS_RELEASE == 'true' }}
|
||||
with:
|
||||
script: |
|
||||
const { VERSION } = process.env
|
||||
const { owner, repo } = context.repo
|
||||
const { sha } = context
|
||||
const ref = `refs/tags/nightly-${VERSION}`
|
||||
github.rest.git.createRef({ owner, repo, sha, ref })
|
||||
name: ${{ env.VERSION }}
|
||||
tag_name: ${{ env.VERSION }}
|
||||
draft: true
|
||||
generate_release_notes: true
|
||||
files: 'out/Zoo*'
|
||||
|
4
.github/workflows/e2e-tests.yml
vendored
@ -68,7 +68,7 @@ jobs:
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
@ -255,7 +255,7 @@ jobs:
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
|
6
.github/workflows/publish-apps-release.yml
vendored
@ -132,12 +132,6 @@ jobs:
|
||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
|
||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
|
||||
|
||||
- name: Upload release files to Github
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: 'out/Zoo*'
|
||||
|
||||
|
||||
announce_release:
|
||||
needs: [publish-apps-release]
|
||||
|
20
README.md
@ -136,7 +136,7 @@ https://github.com/KittyCAD/modeling-app/issues/new
|
||||
|
||||
#### 2. Push a new tag
|
||||
|
||||
Create a new tag and push it to the repo. The `semantic-release.sh` script will automatically bump the minor part, which we use the most. For instance going from `v0.27.0` to `v0.28.0`.
|
||||
Create a new tag and push it to the repo (eg. `v0.28.0` for `$VERSION`)
|
||||
|
||||
```
|
||||
VERSION=$(./scripts/semantic-release.sh)
|
||||
@ -146,14 +146,16 @@ git push origin --tags
|
||||
|
||||
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
|
||||
|
||||
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
|
||||
Once the workflow succeeds, a draft release will be created at https://github.com/KittyCAD/modeling-app/releases.
|
||||
|
||||
#### 3. Manually test artifacts
|
||||
#### 3. Manually test artifacts from the Cut Release PR
|
||||
|
||||
##### Release builds
|
||||
|
||||
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
|
||||
|
||||
Alternatively, the draft release will also include these builds.
|
||||
|
||||
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
|
||||
|
||||
##### Updater-test builds
|
||||
@ -176,11 +178,9 @@ If the prompt doesn't show up, start the app in command line to grab the electro
|
||||
|
||||
#### 4. Publish the release
|
||||
|
||||
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the _Release title_ field as well.
|
||||
Head over to https://github.com/KittyCAD/modeling-app/releases, paste in the changelog discussed in the issue, and publish the draft release created by the `build-apps` workflow from step 2.
|
||||
|
||||
Hit _Generate release notes_ as a starting point to discuss the changelog in the issue. Once done, make sure _Set as the latest release_ is checked, and hit _Publish release_.
|
||||
|
||||
A new `publish-apps-release` will kick in and you should be able to find it [here](https://github.com/KittyCAD/modeling-app/actions?query=event%3Arelease). On success, the files will be uploaded to the public bucket as well as to the GitHub release, and the announcement on Discord will be sent.
|
||||
A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, which can be found under `release` event filter. On success, the files will be uploaded to the public bucket and the announcement on Discord will be sent.
|
||||
|
||||
#### 5. Close the issue
|
||||
|
||||
@ -450,9 +450,3 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
|
||||
## KCL
|
||||
|
||||
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
|
||||
|
||||
### Logging
|
||||
|
||||
To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log some warnings and simple performance metrics. To view these in test runs, use `-- --nocapture`.
|
||||
|
||||
To enable memory metrics, build with `--features dhat-heap`.
|
||||
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 176 KiB |
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 157 KiB |
BIN
assets/icon.ico
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 183 KiB |
BIN
assets/icon.png
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
@ -58,7 +58,7 @@ mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
|
||||
```js
|
||||
// Sketch on the face of a chamfer.
|
||||
fn cube(pos, scale) {
|
||||
fn cube = (pos, scale) => {
|
||||
sg = startSketchOn('XY')
|
||||
|> startProfileAt(pos, %)
|
||||
|> line([0, scale], %)
|
||||
|
@ -37,7 +37,7 @@ assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
|
||||
startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 2 }, %)
|
||||
|> extrude(5, %)
|
||||
|> patternTransform(n, fn(id) {
|
||||
|> patternTransform(n, (id) => {
|
||||
return { translate = [4 * id, 0, 0] }
|
||||
}, %)
|
||||
```
|
||||
|
@ -29,7 +29,7 @@ map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
|
||||
|
||||
```js
|
||||
r = 10 // radius
|
||||
fn drawCircle(id) {
|
||||
fn drawCircle = (id) => {
|
||||
return startSketchOn("XY")
|
||||
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
||||
}
|
||||
@ -45,7 +45,7 @@ circles = map([1..3], drawCircle)
|
||||
```js
|
||||
r = 10 // radius
|
||||
// Call `map`, using an anonymous function instead of a named one.
|
||||
circles = map([1..3], (id) {
|
||||
circles = map([1..3], (id) => {
|
||||
return startSketchOn("XY")
|
||||
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
||||
})
|
||||
|
@ -12,7 +12,7 @@ to other modules.
|
||||
|
||||
```
|
||||
// util.kcl
|
||||
export fn increment(x) {
|
||||
export fn increment = (x) => {
|
||||
return x + 1
|
||||
}
|
||||
```
|
||||
@ -37,11 +37,11 @@ Multiple functions can be exported in a file.
|
||||
|
||||
```
|
||||
// util.kcl
|
||||
export fn increment(x) {
|
||||
export fn increment = (x) => {
|
||||
return x + 1
|
||||
}
|
||||
|
||||
export fn decrement(x) {
|
||||
export fn decrement = (x) => {
|
||||
return x - 1
|
||||
}
|
||||
```
|
||||
|
@ -30,7 +30,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli
|
||||
|
||||
```js
|
||||
// Each instance will be shifted along the X axis.
|
||||
fn transform(id) {
|
||||
fn transform = (id) => {
|
||||
return { translate = [4 * id, 0] }
|
||||
}
|
||||
|
||||
|
@ -30,14 +30,14 @@ reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
|
||||
|
||||
```js
|
||||
// This function adds two numbers.
|
||||
fn add(a, b) {
|
||||
fn add = (a, b) => {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// This function adds an array of numbers.
|
||||
// It uses the `reduce` function, to call the `add` function on every
|
||||
// element of the `arr` parameter. The starting value is 0.
|
||||
fn sum(arr) {
|
||||
fn sum = (arr) => {
|
||||
return reduce(arr, 0, add)
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
|
||||
// an anonymous `add` function as its parameter, instead of declaring a
|
||||
// named function outside.
|
||||
arr = [1, 2, 3]
|
||||
sum = reduce(arr, 0, (i, result_so_far) {
|
||||
sum = reduce(arr, 0, (i, result_so_far) => {
|
||||
return i + result_so_far
|
||||
})
|
||||
|
||||
@ -74,7 +74,7 @@ assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
|
||||
|
||||
```js
|
||||
// Declare a function that sketches a decagon.
|
||||
fn decagon(radius) {
|
||||
fn decagon = (radius) => {
|
||||
// Each side of the decagon is turned this many degrees from the previous angle.
|
||||
stepAngle = 1 / 10 * tau()
|
||||
|
||||
@ -84,7 +84,7 @@ fn decagon(radius) {
|
||||
// Use a `reduce` to draw the remaining decagon sides.
|
||||
// For each number in the array 1..10, run the given function,
|
||||
// which takes a partially-sketched decagon and adds one more edge to it.
|
||||
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) {
|
||||
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) => {
|
||||
// Draw one edge of the decagon.
|
||||
x = cos(stepAngle * i) * radius
|
||||
y = sin(stepAngle * i) * radius
|
||||
|
@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
|
||||
fn cylinder(radius, tag) {
|
||||
fn cylinder = (radius, tag) => {
|
||||
return startSketchAt([0, 0])
|
||||
|> circle({
|
||||
radius = radius,
|
||||
|
@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
|
||||
fn cylinder(radius, tag) {
|
||||
fn cylinder = (radius, tag) => {
|
||||
return startSketchAt([0, 0])
|
||||
|> circle({
|
||||
radius = radius,
|
||||
|
20030
docs/kcl/std.json
@ -41,7 +41,7 @@ If you want to get a value from an array you can use the index like so:
|
||||
An object is defined with `{}` braces. Here is an example object:
|
||||
|
||||
```
|
||||
myObj = { a = 0, b = "thing" }
|
||||
myObj = {a: 0, b: "thing"}
|
||||
```
|
||||
|
||||
We support two different ways of getting properties from objects, you can call
|
||||
@ -54,7 +54,7 @@ We also have support for defining your own functions. Functions can take in any
|
||||
type of argument. Below is an example of the syntax:
|
||||
|
||||
```
|
||||
fn myFn(x) {
|
||||
fn myFn = (x) => {
|
||||
return x
|
||||
}
|
||||
```
|
||||
@ -90,12 +90,12 @@ startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
segAng(rectangleSegmentA001, %) - 90,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
segAng(rectangleSegmentA001, %),
|
||||
-segLen(rectangleSegmentA001, %)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
@ -118,17 +118,17 @@ use the tag `rectangleSegmentA001` in any function or expression in the file.
|
||||
However if the code was written like this:
|
||||
|
||||
```
|
||||
fn rect(origin) {
|
||||
fn rect = (origin) => {
|
||||
return startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
segAng(rectangleSegmentA001, %) - 90,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
segAng(rectangleSegmentA001, %),
|
||||
-segLen(rectangleSegmentA001, %)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
@ -146,17 +146,17 @@ Tags are accessible through the sketch group they are declared in.
|
||||
For example the following code works.
|
||||
|
||||
```
|
||||
fn rect(origin) {
|
||||
fn rect = (origin) => {
|
||||
return startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
segAng(rectangleSegmentA001, %) - 90,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
segAng(rectangleSegmentA001, %),
|
||||
-segLen(rectangleSegmentA001, %)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
@ -167,10 +167,7 @@ myRect = rect([20, 0])
|
||||
|
||||
myRect
|
||||
|> extrude(10, %)
|
||||
|> fillet({
|
||||
radius = 0.5,
|
||||
tags = [myRect.tags.rectangleSegmentA001]
|
||||
}, %)
|
||||
|> fillet({radius: 0.5, tags: [myRect.tags.rectangleSegmentA001]}, %)
|
||||
```
|
||||
|
||||
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside
|
||||
|
161
docs/kcl/types/BinaryOperator.md
Normal file
@ -0,0 +1,161 @@
|
||||
---
|
||||
title: "BinaryOperator"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
Add two numbers.
|
||||
|
||||
**enum:** `+`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Subtract two numbers.
|
||||
|
||||
**enum:** `-`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Multiply two numbers.
|
||||
|
||||
**enum:** `*`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Divide two numbers.
|
||||
|
||||
**enum:** `/`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Modulo two numbers.
|
||||
|
||||
**enum:** `%`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Raise a number to a power.
|
||||
|
||||
**enum:** `^`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Are two numbers equal?
|
||||
|
||||
**enum:** `==`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Are two numbers not equal?
|
||||
|
||||
**enum:** `!=`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Is left greater than right
|
||||
|
||||
**enum:** `>`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Is left greater than or equal to right
|
||||
|
||||
**enum:** `>=`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Is left less than right
|
||||
|
||||
**enum:** `<`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Is left less than or equal to right
|
||||
|
||||
**enum:** `<=`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
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 |
|
||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||
| `raw` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `BinaryExpression`| | No |
|
||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
|
||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `CallExpression`| | No |
|
||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `UnaryExpression`| | No |
|
||||
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No |
|
||||
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `MemberExpression`| | No |
|
||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||
| `computed` |`boolean`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `IfExpression`| | No |
|
||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
||||
| `final_else` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
97
docs/kcl/types/BodyItem.md
Normal file
@ -0,0 +1,97 @@
|
||||
---
|
||||
title: "BodyItem"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ImportStatement`| | No |
|
||||
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
||||
| `path` |`string`| | No |
|
||||
| `raw_path` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ExpressionStatement`| | No |
|
||||
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `VariableDeclaration`| | No |
|
||||
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
||||
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
||||
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ReturnStatement`| | No |
|
||||
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
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`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
318
docs/kcl/types/Expr.md
Normal file
@ -0,0 +1,318 @@
|
||||
---
|
||||
title: "Expr"
|
||||
excerpt: "An expression can be evaluated to yield a single KCL value."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
An expression can be evaluated to yield a single KCL value.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Literal`| | No |
|
||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `raw` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `BinaryExpression`| | No |
|
||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `CallExpression`| | No |
|
||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `PipeExpression`| | No |
|
||||
| `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `PipeSubstitution`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ArrayExpression`| | No |
|
||||
| `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ArrayRangeExpression`| | No |
|
||||
| `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `endInclusive` |`boolean`| Is the `end_element` included in the range? | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ObjectExpression`| | No |
|
||||
| `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No |
|
||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `MemberExpression`| | No |
|
||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `computed` |`boolean`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `UnaryExpression`| | No |
|
||||
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `IfExpression`| | No |
|
||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
||||
| `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `None`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
24
docs/kcl/types/FunctionExpression.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: "FunctionExpression"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||
| `body` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
23
docs/kcl/types/Identifier.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: "Identifier"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
24
docs/kcl/types/ImportItem.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: "ImportItem"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
||||
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
16
docs/kcl/types/ItemVisibility.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "ItemVisibility"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**enum:** `default`, `export`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -317,6 +317,7 @@ Data for an imported geometry.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Function`| | No |
|
||||
| `expression` |[`FunctionExpression`](/docs/kcl/types/FunctionExpression)| Any KCL value. | No |
|
||||
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | 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 |
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Literal`| | No |
|
||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||
| `raw` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
47
docs/kcl/types/LiteralValue.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "LiteralValue"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts any of the following:**
|
||||
|
||||
|
||||
**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 |
|
||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||
| `computed` |`boolean`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
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 |
|
||||
| `startNodes` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
|
||||
|
23
docs/kcl/types/NonCodeNode.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: "NonCodeNode"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
86
docs/kcl/types/NonCodeValue.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
title: "NonCodeValue"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `inlineComment`| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||
|
||||
|
||||
----
|
||||
A block comment. An example of this is the following: ```python,no_run /* This is a block comment */ 1 + 1 ``` Now this is important. The block comment is attached to the next line. This is always the case. Also the block comment doesn't have a new line above it. If it did it would be a `NewLineBlockComment`.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `blockComment`| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||
|
||||
|
||||
----
|
||||
A block comment that has a new line above it. The user explicitly added a new line above the block comment.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `newLineBlockComment`| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `newLine`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
24
docs/kcl/types/ObjectProperty.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: "ObjectProperty"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
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 |
|
||||
|
||||
|
26
docs/kcl/types/Program.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "Program"
|
||||
excerpt: "A KCL program top level, or function body."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
A KCL program top level, or function body.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
|
||||
| `shebang` |[`Shebang`](/docs/kcl/types/Shebang)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
23
docs/kcl/types/Shebang.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: "Shebang"
|
||||
excerpt: "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```"
|
||||
layout: manual
|
||||
---
|
||||
|
||||
A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `content` |`string`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
15
docs/kcl/types/Uint.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "Uint"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `integer` (`uint32`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
|
||||
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
41
docs/kcl/types/VariableKind.md
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
title: "VariableKind"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
Declare a named constant.
|
||||
|
||||
**enum:** `const`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Declare a function.
|
||||
|
||||
**enum:** `fn`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -45,6 +45,7 @@ test.describe('integrations tests', () => {
|
||||
{
|
||||
title: 'test-sample',
|
||||
fileCount: 1,
|
||||
folderCount: 1,
|
||||
},
|
||||
],
|
||||
sortBy: 'last-modified-desc',
|
||||
@ -232,6 +233,7 @@ test.describe('when using the file tree to', () => {
|
||||
{
|
||||
title: projectName,
|
||||
fileCount: 2,
|
||||
folderCount: 2, // TODO: This is a pre-existing bug, there are no folders within the project
|
||||
},
|
||||
],
|
||||
sortBy: 'last-modified-desc',
|
||||
|
@ -4,6 +4,7 @@ import { expect } from '@playwright/test'
|
||||
interface ProjectCardState {
|
||||
title: string
|
||||
fileCount: number
|
||||
folderCount: number
|
||||
}
|
||||
|
||||
interface HomePageState {
|
||||
@ -60,13 +61,15 @@ export class HomePageFixture {
|
||||
const projectCards = await this.projectCard.all()
|
||||
const projectCardStates: Array<ProjectCardState> = []
|
||||
for (const projectCard of projectCards) {
|
||||
const [title, fileCount] = await Promise.all([
|
||||
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
|
||||
|
@ -28,7 +28,6 @@ type SceneSerialised = {
|
||||
|
||||
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
||||
type DblClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
||||
type DragFromHandler = (
|
||||
dragParams: mouseDragFromParams
|
||||
@ -69,7 +68,7 @@ export class SceneFixture {
|
||||
x: number,
|
||||
y: number,
|
||||
{ steps }: { steps: number } = { steps: 20 }
|
||||
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
||||
): [ClickHandler, MoveHandler] =>
|
||||
[
|
||||
(clickParams?: mouseParams) => {
|
||||
if (clickParams?.pixelDiff) {
|
||||
@ -91,16 +90,6 @@ export class SceneFixture {
|
||||
}
|
||||
return this.page.mouse.move(x, y, { steps })
|
||||
},
|
||||
(clickParams?: mouseParams) => {
|
||||
if (clickParams?.pixelDiff) {
|
||||
return doAndWaitForImageDiff(
|
||||
this.page,
|
||||
() => this.page.mouse.dblclick(x, y),
|
||||
clickParams.pixelDiff
|
||||
)
|
||||
}
|
||||
return this.page.mouse.dblclick(x, y)
|
||||
},
|
||||
] as const
|
||||
makeDragHelpers = (
|
||||
x: number,
|
||||
|
@ -6,7 +6,6 @@ export class ToolbarFixture {
|
||||
public page: Page
|
||||
|
||||
extrudeButton!: Locator
|
||||
loftButton!: Locator
|
||||
offsetPlaneButton!: Locator
|
||||
startSketchBtn!: Locator
|
||||
lineBtn!: Locator
|
||||
@ -27,7 +26,6 @@ export class ToolbarFixture {
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
this.extrudeButton = page.getByTestId('extrude')
|
||||
this.loftButton = page.getByTestId('loft')
|
||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||
this.startSketchBtn = page.getByTestId('sketch')
|
||||
this.lineBtn = page.getByTestId('line')
|
||||
|
@ -552,82 +552,6 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||
})
|
||||
})
|
||||
|
||||
test(`Verify user can double-click to edit a sketch`, async ({
|
||||
app,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const initialCode = `closedSketch = startSketchOn('XZ')
|
||||
|> circle({ center = [8, 5], radius = 2 }, %)
|
||||
openSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-5, 0], %)
|
||||
|> lineTo([0, 5], %)
|
||||
|> xLine(5, %)
|
||||
|> tangentialArcTo([10, 0], %)
|
||||
`
|
||||
await app.initialise(initialCode)
|
||||
|
||||
const pointInsideCircle = {
|
||||
x: app.viewPortSize.width * 0.63,
|
||||
y: app.viewPortSize.height * 0.5,
|
||||
}
|
||||
const pointOnPathAfterSketching = {
|
||||
x: app.viewPortSize.width * 0.58,
|
||||
y: app.viewPortSize.height * 0.5,
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_clickOpenPath, moveToOpenPath, dblClickOpenPath] =
|
||||
scene.makeMouseHelpers(
|
||||
pointOnPathAfterSketching.x,
|
||||
pointOnPathAfterSketching.y
|
||||
)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_clickCircle, moveToCircle, dblClickCircle] = scene.makeMouseHelpers(
|
||||
pointInsideCircle.x,
|
||||
pointInsideCircle.y
|
||||
)
|
||||
|
||||
const exitSketch = async () => {
|
||||
await test.step(`Exit sketch mode`, async () => {
|
||||
await toolbar.exitSketchBtn.click()
|
||||
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
||||
await expect(toolbar.startSketchBtn).toBeEnabled()
|
||||
})
|
||||
}
|
||||
|
||||
await test.step(`Double-click on the closed sketch`, async () => {
|
||||
await moveToCircle()
|
||||
await dblClickCircle()
|
||||
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||
await editor.expectState({
|
||||
activeLines: [`|>circle({center=[8,5],radius=2},%)`],
|
||||
highlightedCode: 'circle({center=[8,5],radius=2},%)',
|
||||
diagnostics: [],
|
||||
})
|
||||
})
|
||||
|
||||
await exitSketch()
|
||||
|
||||
await test.step(`Double-click on the open sketch`, async () => {
|
||||
await moveToOpenPath()
|
||||
await scene.expectPixelColor([250, 250, 250], pointOnPathAfterSketching, 15)
|
||||
// There is a full execution after exiting sketch that clears the scene.
|
||||
await app.page.waitForTimeout(500)
|
||||
await dblClickOpenPath()
|
||||
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||
// Wait for enter sketch mode to complete
|
||||
await app.page.waitForTimeout(500)
|
||||
await editor.expectState({
|
||||
activeLines: [`|>xLine(5,%)`],
|
||||
highlightedCode: 'xLine(5,%)',
|
||||
diagnostics: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test(`Offset plane point-and-click`, async ({
|
||||
app,
|
||||
scene,
|
||||
@ -677,94 +601,3 @@ test(`Offset plane point-and-click`, async ({
|
||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||
})
|
||||
})
|
||||
|
||||
const loftPointAndClickCases = [
|
||||
{ shouldPreselect: true },
|
||||
{ shouldPreselect: false },
|
||||
]
|
||||
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
||||
app,
|
||||
page,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||
plane001 = offsetPlane('XZ', 50)
|
||||
sketch002 = startSketchOn(plane001)
|
||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||
`
|
||||
await app.initialise(initialCode)
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 575, y: 200 }
|
||||
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const [clickOnSketch2] = scene.makeMouseHelpers(
|
||||
testPoint.x,
|
||||
testPoint.y + 80
|
||||
)
|
||||
const loftDeclaration = 'loft001 = loft([sketch001, sketch002])'
|
||||
|
||||
await test.step(`Look for the white of the sketch001 shape`, async () => {
|
||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||
})
|
||||
|
||||
async function selectSketches() {
|
||||
await clickOnSketch1()
|
||||
await page.keyboard.down('Shift')
|
||||
await clickOnSketch2()
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.keyboard.up('Shift')
|
||||
}
|
||||
|
||||
if (!shouldPreselect) {
|
||||
await test.step(`Go through the command bar flow without preselected sketches`, async () => {
|
||||
await toolbar.loftButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'selection',
|
||||
currentArgValue: '',
|
||||
headerArguments: { Selection: '' },
|
||||
highlightedHeaderArg: 'selection',
|
||||
commandName: 'Loft',
|
||||
})
|
||||
await selectSketches()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: { Selection: '2 faces' },
|
||||
commandName: 'Loft',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
})
|
||||
} else {
|
||||
await test.step(`Preselect the two sketches`, async () => {
|
||||
await selectSketches()
|
||||
})
|
||||
|
||||
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
||||
await toolbar.loftButton.click()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: { Selection: '2 faces' },
|
||||
commandName: 'Loft',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
})
|
||||
}
|
||||
|
||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||
await editor.expectEditor.toContain(loftDeclaration)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: [loftDeclaration],
|
||||
highlightedCode: '',
|
||||
})
|
||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -192,7 +192,7 @@
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
"eslint-plugin-import": "^2.30.0",
|
||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||
"happy-dom": "^15.11.7",
|
||||
"happy-dom": "^15.10.2",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^9.1.5",
|
||||
"kill-port": "^2.0.1",
|
||||
@ -212,7 +212,7 @@
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^1.6.0",
|
||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||
"wasm-pack": "^0.13.1",
|
||||
"wasm-pack": "^0.13.0",
|
||||
"ws": "^8.17.0",
|
||||
"yarn": "^1.22.22"
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
export VERSION=$(date +'%-y.%-m.%-d')
|
||||
export TAG="nightly-v$VERSION"
|
||||
export PREVIOUS_TAG=$(git describe --tags --match="nightly-v[0-9]*" --abbrev=0)
|
||||
export COMMIT=$(git rev-parse --short HEAD)
|
||||
|
||||
# package.json
|
||||
@ -15,7 +13,7 @@ yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' ele
|
||||
yq -i '.appId = "dev.zoo.modeling-app-nightly"' electron-builder.yml
|
||||
|
||||
# Release notes
|
||||
./scripts/get-nightly-changelog.sh > release-notes.md
|
||||
echo "Nightly build $VERSION (commit $COMMIT)" > release-notes.md
|
||||
|
||||
# icons
|
||||
cp assets/icon-nightly.png assets/icon.png
|
||||
|
@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
echo "## What's Changed"
|
||||
git log ${PREVIOUS_TAG}..HEAD --oneline --pretty=format:%s | grep -v Bump | awk '{print "* "toupper(substr($0,0,1))substr($0,2)}'
|
||||
echo ""
|
||||
echo "**Full Changelog**: https://github.com/KittyCAD/modeling-app/compare/${PREVIOUS_TAG}...${TAG}"
|
@ -273,26 +273,14 @@ export class CameraControls {
|
||||
camSettings.center.y,
|
||||
camSettings.center.z
|
||||
)
|
||||
const orientation = new Quaternion(
|
||||
const quat = new Quaternion(
|
||||
camSettings.orientation.x,
|
||||
camSettings.orientation.y,
|
||||
camSettings.orientation.z,
|
||||
camSettings.orientation.w
|
||||
).invert()
|
||||
|
||||
const newUp = new Vector3(
|
||||
camSettings.up.x,
|
||||
camSettings.up.y,
|
||||
camSettings.up.z
|
||||
)
|
||||
this.camera.quaternion.set(
|
||||
orientation.x,
|
||||
orientation.y,
|
||||
orientation.z,
|
||||
orientation.w
|
||||
)
|
||||
this.camera.up.copy(newUp)
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.camera.up.copy(new Vector3(0, 1, 0).applyQuaternion(quat))
|
||||
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
||||
this.useOrthographicCamera()
|
||||
}
|
||||
@ -1176,7 +1164,7 @@ export class CameraControls {
|
||||
this.camera.updateProjectionMatrix()
|
||||
}
|
||||
|
||||
if (this.syncDirection === 'clientToEngine' || forceUpdate) {
|
||||
if (this.syncDirection === 'clientToEngine' || forceUpdate)
|
||||
this.throttledUpdateEngineCamera({
|
||||
quaternion: this.camera.quaternion,
|
||||
position: this.camera.position,
|
||||
@ -1184,7 +1172,6 @@ export class CameraControls {
|
||||
isPerspective: this.isPerspective,
|
||||
target: this.target,
|
||||
})
|
||||
}
|
||||
this.deferReactUpdate(this.reactCameraProperties)
|
||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||
}
|
||||
|
@ -50,7 +50,6 @@ import {
|
||||
isSketchPipe,
|
||||
Selections,
|
||||
updateSelections,
|
||||
canLoftSelection,
|
||||
} from 'lib/selections'
|
||||
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||
@ -83,7 +82,7 @@ import { getVarNameModal } from 'hooks/useToolbarGuards'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { modelingMachineEvent } from 'editor/manager'
|
||||
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addEdgeTreatment'
|
||||
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||
import {
|
||||
ExportIntent,
|
||||
EngineConnectionStateType,
|
||||
@ -570,21 +569,6 @@ export const ModelingMachineProvider = ({
|
||||
if (err(canSweep)) return false
|
||||
return canSweep
|
||||
},
|
||||
'has valid loft selection': ({ context: { selectionRanges } }) => {
|
||||
const hasNoSelection =
|
||||
selectionRanges.graphSelections.length === 0 ||
|
||||
isRangeBetweenCharacters(selectionRanges) ||
|
||||
isSelectionLastLine(selectionRanges, codeManager.code)
|
||||
|
||||
if (hasNoSelection) {
|
||||
const count = 2
|
||||
return doesSceneHaveSweepableSketch(kclManager.ast, count)
|
||||
}
|
||||
|
||||
const canLoft = canLoftSelection(selectionRanges)
|
||||
if (err(canLoft)) return false
|
||||
return canLoft
|
||||
},
|
||||
'has valid selection for deletion': ({
|
||||
context: { selectionRanges },
|
||||
}) => {
|
||||
@ -592,10 +576,8 @@ export const ModelingMachineProvider = ({
|
||||
if (selectionRanges.graphSelections.length <= 0) return false
|
||||
return true
|
||||
},
|
||||
'has valid edge treatment selection': ({
|
||||
context: { selectionRanges },
|
||||
}) => {
|
||||
return hasValidEdgeTreatmentSelection({
|
||||
'has valid fillet selection': ({ context: { selectionRanges } }) => {
|
||||
return hasValidFilletSelection({
|
||||
selectionRanges,
|
||||
ast: kclManager.ast,
|
||||
code: codeManager.code,
|
||||
|
@ -18,8 +18,6 @@ import { useRouteLoaderData } from 'react-router-dom'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { IndexLoaderData } from 'lib/types'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { err, reportRejection } from 'lib/trap'
|
||||
import { getArtifactOfTypes } from 'lang/std/artifactGraph'
|
||||
|
||||
enum StreamState {
|
||||
Playing = 'playing',
|
||||
@ -282,49 +280,12 @@ export const Stream = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On double-click of sketch entities we automatically enter sketch mode with the selected sketch,
|
||||
* allowing for quick editing of sketches. TODO: This should be moved to a more central place.
|
||||
*/
|
||||
const enterSketchModeIfSelectingSketch: MouseEventHandler<HTMLDivElement> = (
|
||||
e
|
||||
) => {
|
||||
if (
|
||||
!isNetworkOkay ||
|
||||
!videoRef.current ||
|
||||
state.matches('Sketch') ||
|
||||
state.matches({ idle: 'showPlanes' }) ||
|
||||
sceneInfra.camControls.wasDragging === true ||
|
||||
!btnName(e.nativeEvent).left
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
sendSelectEventToEngine(e, videoRef.current)
|
||||
.then(({ entity_id }) => {
|
||||
if (!entity_id) {
|
||||
// No entity selected. This is benign
|
||||
return
|
||||
}
|
||||
const path = getArtifactOfTypes(
|
||||
{ key: entity_id, types: ['path', 'solid2D', 'segment'] },
|
||||
engineCommandManager.artifactGraph
|
||||
)
|
||||
if (err(path)) {
|
||||
return path
|
||||
}
|
||||
sceneInfra.modelingSend({ type: 'Enter sketch' })
|
||||
})
|
||||
.catch(reportRejection)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="absolute inset-0 z-0"
|
||||
id="stream"
|
||||
data-testid="stream"
|
||||
onClick={handleMouseUp}
|
||||
onDoubleClick={enterSketchModeIfSelectingSketch}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
onContextMenuCapture={(e) => e.preventDefault()}
|
||||
>
|
||||
|
@ -384,6 +384,7 @@ const myVar = funcN(1, 2)`
|
||||
raw: '2',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -464,6 +465,7 @@ describe('testing pipe operator special', () => {
|
||||
],
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
type: 'CallExpression',
|
||||
@ -506,6 +508,7 @@ describe('testing pipe operator special', () => {
|
||||
end: 60,
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
type: 'CallExpression',
|
||||
@ -553,6 +556,7 @@ describe('testing pipe operator special', () => {
|
||||
value: 'myPath',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
type: 'CallExpression',
|
||||
@ -594,6 +598,7 @@ describe('testing pipe operator special', () => {
|
||||
end: 115,
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
type: 'CallExpression',
|
||||
@ -620,6 +625,7 @@ describe('testing pipe operator special', () => {
|
||||
end: 130,
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -705,6 +711,7 @@ describe('testing pipe operator special', () => {
|
||||
end: 35,
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -1758,6 +1765,7 @@ describe('test UnaryExpression', () => {
|
||||
raw: '100',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
@ -1829,9 +1837,11 @@ describe('testing nested call expressions', () => {
|
||||
raw: '3',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1869,6 +1879,7 @@ describe('should recognise callExpresions in binaryExpressions', () => {
|
||||
name: 'seg02',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
right: {
|
||||
type: 'Literal',
|
||||
|
@ -346,37 +346,6 @@ export function extrudeSketch(
|
||||
}
|
||||
}
|
||||
|
||||
export function loftSketches(
|
||||
node: Node<Program>,
|
||||
declarators: VariableDeclarator[]
|
||||
): {
|
||||
modifiedAst: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
} {
|
||||
const modifiedAst = structuredClone(node)
|
||||
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.LOFT)
|
||||
const elements = declarators.map((d) => createIdentifier(d.id.name))
|
||||
const loft = createCallExpressionStdLib('loft', [
|
||||
createArrayExpression(elements),
|
||||
])
|
||||
const declaration = createVariableDeclaration(name, loft)
|
||||
modifiedAst.body.push(declaration)
|
||||
const pathToNode: PathToNode = [
|
||||
['body', ''],
|
||||
[modifiedAst.body.length - 1, 'index'],
|
||||
['declarations', 'VariableDeclaration'],
|
||||
['0', 'index'],
|
||||
['init', 'VariableDeclarator'],
|
||||
['arguments', 'CallExpression'],
|
||||
[0, 'index'],
|
||||
]
|
||||
|
||||
return {
|
||||
modifiedAst,
|
||||
pathToNode,
|
||||
}
|
||||
}
|
||||
|
||||
export function revolveSketch(
|
||||
node: Node<Program>,
|
||||
pathToNode: PathToNode,
|
||||
@ -758,6 +727,7 @@ export function createCallExpressionStdLib(
|
||||
|
||||
name,
|
||||
},
|
||||
optional: false,
|
||||
arguments: args,
|
||||
}
|
||||
}
|
||||
@ -779,6 +749,7 @@ export function createCallExpression(
|
||||
|
||||
name,
|
||||
},
|
||||
optional: false,
|
||||
arguments: args,
|
||||
}
|
||||
}
|
||||
|
@ -10,21 +10,18 @@ import {
|
||||
VariableDeclarator,
|
||||
} from '../wasm'
|
||||
import {
|
||||
EdgeTreatmentType,
|
||||
getPathToExtrudeForSegmentSelection,
|
||||
hasValidEdgeTreatmentSelection,
|
||||
isTagUsedInEdgeTreatment,
|
||||
modifyAstWithEdgeTreatmentAndTag,
|
||||
FilletParameters,
|
||||
ChamferParameters,
|
||||
EdgeTreatmentParameters,
|
||||
} from './addEdgeTreatment'
|
||||
hasValidFilletSelection,
|
||||
isTagUsedInFillet,
|
||||
modifyAstWithFilletAndTag,
|
||||
} from './addFillet'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||
import { createLiteral } from 'lang/modifyAst'
|
||||
import { err } from 'lib/trap'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||
import { VITE_KC_DEV_TOKEN } from 'env'
|
||||
import { KclCommandValue } from 'lib/commandTypes'
|
||||
import { isOverlap } from 'lib/utils'
|
||||
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
||||
|
||||
@ -256,10 +253,10 @@ extrude003 = extrude(-15, sketch003)`
|
||||
})
|
||||
})
|
||||
|
||||
const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||
const runModifyAstCloneWithFilletAndTag = async (
|
||||
code: string,
|
||||
selectionSnippets: Array<string>,
|
||||
parameters: EdgeTreatmentParameters,
|
||||
radiusValue: number,
|
||||
expectedCode: string
|
||||
) => {
|
||||
// ast
|
||||
@ -277,6 +274,13 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||
]
|
||||
)
|
||||
|
||||
// radius
|
||||
const radius: KclCommandValue = {
|
||||
valueAst: createLiteral(radiusValue),
|
||||
valueText: radiusValue.toString(),
|
||||
valueCalculated: radiusValue.toString(),
|
||||
}
|
||||
|
||||
// executeAst
|
||||
await kclManager.executeAst({ ast })
|
||||
const artifactGraph = engineCommandManager.artifactGraph
|
||||
@ -295,8 +299,8 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||
otherSelections: [],
|
||||
}
|
||||
|
||||
// apply edge treatment to seleciton
|
||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||
// apply fillet to selection
|
||||
const result = modifyAstWithFilletAndTag(ast, selection, radius)
|
||||
if (err(result)) {
|
||||
return result
|
||||
}
|
||||
@ -306,41 +310,8 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||
|
||||
expect(newCode).toContain(expectedCode)
|
||||
}
|
||||
const createFilletParameters = (radiusValue: number): FilletParameters => ({
|
||||
type: EdgeTreatmentType.Fillet,
|
||||
radius: {
|
||||
valueAst: createLiteral(radiusValue),
|
||||
valueText: radiusValue.toString(),
|
||||
valueCalculated: radiusValue.toString(),
|
||||
},
|
||||
})
|
||||
const createChamferParameters = (lengthValue: number): ChamferParameters => ({
|
||||
type: EdgeTreatmentType.Chamfer,
|
||||
length: {
|
||||
valueAst: createLiteral(lengthValue),
|
||||
valueText: lengthValue.toString(),
|
||||
valueCalculated: lengthValue.toString(),
|
||||
},
|
||||
})
|
||||
// Iterate tests over all edge treatment types
|
||||
Object.values(EdgeTreatmentType).forEach(
|
||||
(edgeTreatmentType: EdgeTreatmentType) => {
|
||||
// create parameters based on the edge treatment type
|
||||
let parameterName: string
|
||||
let parameters: EdgeTreatmentParameters
|
||||
if (edgeTreatmentType === EdgeTreatmentType.Fillet) {
|
||||
parameterName = 'radius'
|
||||
parameters = createFilletParameters(3)
|
||||
} else if (edgeTreatmentType === EdgeTreatmentType.Chamfer) {
|
||||
parameterName = 'length'
|
||||
parameters = createChamferParameters(3)
|
||||
} else {
|
||||
// Handle future edge treatments
|
||||
return new Error(`Unsupported edge treatment type: ${edgeTreatmentType}`)
|
||||
}
|
||||
// run tests
|
||||
describe(`Testing modifyAstCloneWithEdgeTreatmentAndTag with ${edgeTreatmentType}s`, () => {
|
||||
it(`should add a ${edgeTreatmentType} to a specific segment`, async () => {
|
||||
describe('Testing applyFilletToSelection', () => {
|
||||
it('should add a fillet to a specific segment', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -350,6 +321,7 @@ Object.values(EdgeTreatmentType).forEach(
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([0, -20], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -358,16 +330,16 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} to the sketch pipe`, async () => {
|
||||
it('should add a fillet to the sketch pipe', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -377,6 +349,7 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
|> extrude(-15, %)`
|
||||
const segmentSnippets = ['line([0, -20], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -385,16 +358,16 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
|> extrude(-15, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} to an already tagged segment`, async () => {
|
||||
it('should add a fillet to an already tagged segment', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -404,6 +377,7 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([0, -20], %, $seg01)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -412,16 +386,16 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} with existing tag on other segment`, async () => {
|
||||
it('should add a fillet with existing tag on other segment', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -431,6 +405,7 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -439,16 +414,16 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||
|> fillet({ radius = 3, tags = [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} with existing fillet on other segment`, async () => {
|
||||
it('should add a fillet with existing fillet on other segment', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -459,6 +434,7 @@ extrude001 = extrude(-15, sketch001)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 5, tags = [seg01] }, %)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -468,45 +444,16 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 5, tags = [seg01] }, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||
|> fillet({ radius = 3, tags = [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} with existing chamfer on other segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> chamfer({ length: 5, tags: [seg01] }, %)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
|> line([-20, 0], %, $seg02)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> chamfer({ length: 5, tags: [seg01] }, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName}: 3, tags: [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} to two segments of a single extrusion`, async () => {
|
||||
it('should add a fillet to two segments of a single extrusion', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -516,6 +463,7 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([20, 0], %)', 'line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -524,16 +472,16 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01, seg02] }, %)`
|
||||
|> fillet({ radius = 3, tags = [seg01, seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add ${edgeTreatmentType}s to two bodies`, async () => {
|
||||
it('should add fillets to two bodies', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -555,6 +503,7 @@ extrude002 = extrude(-25, sketch002)` // <--- body 2
|
||||
'line([-20, 0], %)',
|
||||
'line([0, -15], %)',
|
||||
]
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -563,7 +512,7 @@ extrude002 = extrude(-25, sketch002)` // <--- body 2
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01, seg02] }, %)
|
||||
|> fillet({ radius = 3, tags = [seg01, seg02] }, %)
|
||||
sketch002 = startSketchOn('XY')
|
||||
|> startProfileAt([30, 10], %)
|
||||
|> line([15, 0], %)
|
||||
@ -572,20 +521,18 @@ sketch002 = startSketchOn('XY')
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude002 = extrude(-25, sketch002)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg03] }, %)` // <-- able to add a new one
|
||||
|> fillet({ radius = 3, tags = [seg03] }, %)` // <-- able to add a new one
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe('Testing isTagUsedInEdgeTreatment', () => {
|
||||
describe('Testing isTagUsedInFillet', () => {
|
||||
const code = `sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.72, 4.13], %)
|
||||
|> line([7.11, 3.48], %, $seg01)
|
||||
@ -618,7 +565,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
||||
})
|
||||
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
||||
@ -637,7 +584,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
||||
})
|
||||
it('should correctly identify no edges', () => {
|
||||
@ -656,7 +603,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual([])
|
||||
})
|
||||
})
|
||||
@ -691,7 +638,7 @@ describe('Testing button states', () => {
|
||||
}
|
||||
|
||||
// state
|
||||
const buttonState = hasValidEdgeTreatmentSelection({
|
||||
const buttonState = hasValidFilletSelection({
|
||||
ast,
|
||||
selectionRanges,
|
||||
code,
|
@ -44,49 +44,32 @@ import {
|
||||
} from 'lib/singletons'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
// Edge Treatment Types
|
||||
export enum EdgeTreatmentType {
|
||||
Chamfer = 'chamfer',
|
||||
Fillet = 'fillet',
|
||||
}
|
||||
// Apply Fillet To Selection
|
||||
|
||||
export interface ChamferParameters {
|
||||
type: EdgeTreatmentType.Chamfer
|
||||
length: KclCommandValue
|
||||
}
|
||||
export interface FilletParameters {
|
||||
type: EdgeTreatmentType.Fillet
|
||||
radius: KclCommandValue
|
||||
}
|
||||
export type EdgeTreatmentParameters = ChamferParameters | FilletParameters
|
||||
|
||||
// Apply Edge Treatment (Fillet or Chamfer) To Selection
|
||||
export function applyEdgeTreatmentToSelection(
|
||||
export function applyFilletToSelection(
|
||||
ast: Node<Program>,
|
||||
selection: Selections,
|
||||
parameters: EdgeTreatmentParameters
|
||||
radius: KclCommandValue
|
||||
): void | Error {
|
||||
// 1. clone and modify with edge treatment and tag
|
||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||
// 1. clone and modify with fillet and tag
|
||||
const result = modifyAstWithFilletAndTag(ast, selection, radius)
|
||||
if (err(result)) return result
|
||||
const { modifiedAst, pathToEdgeTreatmentNode } = result
|
||||
const { modifiedAst, pathToFilletNode } = result
|
||||
|
||||
// 2. update ast
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
updateAstAndFocus(modifiedAst, pathToEdgeTreatmentNode)
|
||||
updateAstAndFocus(modifiedAst, pathToFilletNode)
|
||||
}
|
||||
|
||||
export function modifyAstWithEdgeTreatmentAndTag(
|
||||
export function modifyAstWithFilletAndTag(
|
||||
ast: Node<Program>,
|
||||
selections: Selections,
|
||||
parameters: EdgeTreatmentParameters
|
||||
):
|
||||
| { modifiedAst: Node<Program>; pathToEdgeTreatmentNode: Array<PathToNode> }
|
||||
| Error {
|
||||
radius: KclCommandValue
|
||||
): { modifiedAst: Node<Program>; pathToFilletNode: Array<PathToNode> } | Error {
|
||||
let clonedAst = structuredClone(ast)
|
||||
const clonedAstForGetExtrude = structuredClone(ast)
|
||||
|
||||
const astResult = insertParametersIntoAst(clonedAst, parameters)
|
||||
const astResult = insertRadiusIntoAst(clonedAst, radius)
|
||||
if (err(astResult)) return astResult
|
||||
|
||||
const artifactGraph = engineCommandManager.artifactGraph
|
||||
@ -136,26 +119,21 @@ export function modifyAstWithEdgeTreatmentAndTag(
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Apply edge treatments for each extrude node (body)
|
||||
let pathToEdgeTreatmentNodes: Array<PathToNode> = []
|
||||
// Step 2: Apply fillet(s) for each extrude node (body)
|
||||
let pathToFilletNodes: Array<PathToNode> = []
|
||||
for (const [pathToExtrudeNode, tagInfos] of extrudeToTagsMap.entries()) {
|
||||
// Create an edge treatment expression with multiple tags
|
||||
// Create a fillet expression with multiple tags
|
||||
const radiusValue =
|
||||
'variableName' in radius ? radius.variableIdentifierAst : radius.valueAst
|
||||
|
||||
// edge treatment parameter
|
||||
const parameterResult = getParameterNameAndValue(parameters)
|
||||
if (err(parameterResult)) return parameterResult
|
||||
const { parameterName, parameterValue } = parameterResult
|
||||
|
||||
// tag calls
|
||||
const tagCalls = tagInfos.map(({ tag, artifact }) => {
|
||||
return getEdgeTagCall(tag, artifact)
|
||||
})
|
||||
const firstTag = tagCalls[0] // can be Identifier or CallExpression (for opposite and adjacent edges)
|
||||
|
||||
// edge treatment call
|
||||
const edgeTreatmentCall = createCallExpressionStdLib(parameters.type, [
|
||||
const filletCall = createCallExpressionStdLib('fillet', [
|
||||
createObjectExpression({
|
||||
[parameterName]: parameterValue,
|
||||
radius: radiusValue,
|
||||
tags: createArrayExpression(tagCalls),
|
||||
}),
|
||||
createPipeSubstitution(),
|
||||
@ -169,89 +147,64 @@ export function modifyAstWithEdgeTreatmentAndTag(
|
||||
if (err(locatedExtrudeDeclarator)) return locatedExtrudeDeclarator
|
||||
const { extrudeDeclarator } = locatedExtrudeDeclarator
|
||||
|
||||
// Modify the extrude expression to include this edge treatment expression
|
||||
// CallExpression - no edge treatment
|
||||
// PipeExpression - edge treatment exists or body in sketch pipe
|
||||
// Modify the extrude expression to include this fillet expression
|
||||
// CallExpression - no fillet
|
||||
// PipeExpression - fillet exists or extrude in sketch pipe
|
||||
|
||||
let pathToEdgeTreatmentNode: PathToNode
|
||||
let pathToFilletNode: PathToNode = []
|
||||
|
||||
if (extrudeDeclarator.init.type === 'CallExpression') {
|
||||
// 1. case when no edge treatment exists
|
||||
// 1. case when no fillet exists
|
||||
|
||||
// modify ast with new edge treatment call by mutating the extrude node
|
||||
// modify ast with new fillet call by mutating the extrude node
|
||||
extrudeDeclarator.init = createPipeExpression([
|
||||
extrudeDeclarator.init,
|
||||
edgeTreatmentCall,
|
||||
filletCall,
|
||||
])
|
||||
|
||||
// get path to the edge treatment node
|
||||
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||
// get path to the fillet node
|
||||
pathToFilletNode = getPathToNodeOfFilletLiteral(
|
||||
pathToExtrudeNode,
|
||||
extrudeDeclarator,
|
||||
firstTag,
|
||||
parameters
|
||||
firstTag
|
||||
)
|
||||
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||
pathToFilletNodes.push(pathToFilletNode)
|
||||
} else if (extrudeDeclarator.init.type === 'PipeExpression') {
|
||||
// 2. case when edge treatment exists or extrude in sketch pipe
|
||||
// 2. case when fillet exists or extrude in sketch pipe
|
||||
|
||||
// mutate the extrude node with the new edge treatment call
|
||||
extrudeDeclarator.init.body.push(edgeTreatmentCall)
|
||||
// mutate the extrude node with the new fillet call
|
||||
extrudeDeclarator.init.body.push(filletCall)
|
||||
|
||||
// get path to the edge treatment node
|
||||
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||
// get path to the fillet node
|
||||
pathToFilletNode = getPathToNodeOfFilletLiteral(
|
||||
pathToExtrudeNode,
|
||||
extrudeDeclarator,
|
||||
firstTag,
|
||||
parameters
|
||||
firstTag
|
||||
)
|
||||
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||
pathToFilletNodes.push(pathToFilletNode)
|
||||
} else {
|
||||
return new Error('Unsupported extrude type.')
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: clonedAst,
|
||||
pathToEdgeTreatmentNode: pathToEdgeTreatmentNodes,
|
||||
}
|
||||
return { modifiedAst: clonedAst, pathToFilletNode: pathToFilletNodes }
|
||||
}
|
||||
|
||||
function insertParametersIntoAst(
|
||||
function insertRadiusIntoAst(
|
||||
ast: Program,
|
||||
parameters: EdgeTreatmentParameters
|
||||
radius: KclCommandValue
|
||||
): { ast: Program } | Error {
|
||||
try {
|
||||
// Validate and update AST
|
||||
if (
|
||||
'variableName' in radius &&
|
||||
radius.variableName &&
|
||||
radius.insertIndex !== undefined
|
||||
) {
|
||||
const newAst = structuredClone(ast)
|
||||
|
||||
// handle radius parameter
|
||||
if (
|
||||
parameters.type === EdgeTreatmentType.Fillet &&
|
||||
'variableName' in parameters.radius &&
|
||||
parameters.radius.variableName &&
|
||||
parameters.radius.insertIndex !== undefined
|
||||
) {
|
||||
newAst.body.splice(
|
||||
parameters.radius.insertIndex,
|
||||
0,
|
||||
parameters.radius.variableDeclarationAst
|
||||
)
|
||||
}
|
||||
// handle length parameter
|
||||
if (
|
||||
parameters.type === EdgeTreatmentType.Chamfer &&
|
||||
'variableName' in parameters.length &&
|
||||
parameters.length.variableName &&
|
||||
parameters.length.insertIndex !== undefined
|
||||
) {
|
||||
newAst.body.splice(
|
||||
parameters.length.insertIndex,
|
||||
0,
|
||||
parameters.length.variableDeclarationAst
|
||||
)
|
||||
}
|
||||
|
||||
// handle upcoming parameters here (for blend, bevel, etc.)
|
||||
newAst.body.splice(radius.insertIndex, 0, radius.variableDeclarationAst)
|
||||
return { ast: newAst }
|
||||
}
|
||||
return { ast }
|
||||
} catch (error) {
|
||||
return new Error(`Failed to handle AST: ${(error as Error).message}`)
|
||||
}
|
||||
@ -295,10 +248,10 @@ export function getPathToExtrudeForSegmentSelection(
|
||||
|
||||
async function updateAstAndFocus(
|
||||
modifiedAst: Node<Program>,
|
||||
pathToEdgeTreatmentNode: Array<PathToNode>
|
||||
pathToFilletNode: Array<PathToNode>
|
||||
) {
|
||||
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||
focusPath: pathToEdgeTreatmentNode,
|
||||
focusPath: pathToFilletNode,
|
||||
})
|
||||
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
|
||||
@ -387,38 +340,27 @@ function locateExtrudeDeclarator(
|
||||
return { extrudeDeclarator }
|
||||
}
|
||||
|
||||
function getPathToNodeOfEdgeTreatmentLiteral(
|
||||
function getPathToNodeOfFilletLiteral(
|
||||
pathToExtrudeNode: PathToNode,
|
||||
extrudeDeclarator: VariableDeclarator,
|
||||
tag: Identifier | CallExpression,
|
||||
parameters: EdgeTreatmentParameters
|
||||
tag: Identifier | CallExpression
|
||||
): PathToNode {
|
||||
let pathToEdgeTreatmentObj: PathToNode = []
|
||||
let inEdgeTreatment = false
|
||||
let pathToFilletObj: PathToNode = []
|
||||
let inFillet = false
|
||||
|
||||
traverse(extrudeDeclarator.init, {
|
||||
enter(node, path) {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.name === parameters.type
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
}
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (!hasTag(node, tag)) return false
|
||||
pathToEdgeTreatmentObj = getPathToEdgeTreatmentParameterLiteral(
|
||||
node,
|
||||
path,
|
||||
parameters
|
||||
)
|
||||
pathToFilletObj = getPathToRadiusLiteral(node, path)
|
||||
}
|
||||
},
|
||||
leave(node) {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.name === parameters.type
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
}
|
||||
},
|
||||
})
|
||||
@ -433,7 +375,7 @@ function getPathToNodeOfEdgeTreatmentLiteral(
|
||||
|
||||
return [
|
||||
...pathToExtrudeNode.slice(0, indexOfPipeExpression),
|
||||
...pathToEdgeTreatmentObj,
|
||||
...pathToFilletObj,
|
||||
]
|
||||
}
|
||||
|
||||
@ -466,62 +408,23 @@ function hasTag(
|
||||
})
|
||||
}
|
||||
|
||||
function getPathToEdgeTreatmentParameterLiteral(
|
||||
node: ObjectExpression,
|
||||
path: any,
|
||||
parameters: EdgeTreatmentParameters
|
||||
): PathToNode {
|
||||
let pathToEdgeTreatmentObj = path
|
||||
const parameterResult = getParameterNameAndValue(parameters)
|
||||
if (err(parameterResult)) return pathToEdgeTreatmentObj
|
||||
const { parameterName } = parameterResult
|
||||
|
||||
function getPathToRadiusLiteral(node: ObjectExpression, path: any): PathToNode {
|
||||
let pathToFilletObj = path
|
||||
node.properties.forEach((prop, index) => {
|
||||
if (prop.key.name === parameterName) {
|
||||
pathToEdgeTreatmentObj.push(
|
||||
if (prop.key.name === 'radius') {
|
||||
pathToFilletObj.push(
|
||||
['properties', 'ObjectExpression'],
|
||||
[index, 'index'],
|
||||
['value', 'Property']
|
||||
)
|
||||
}
|
||||
})
|
||||
return pathToEdgeTreatmentObj
|
||||
}
|
||||
|
||||
function getParameterNameAndValue(
|
||||
parameters: EdgeTreatmentParameters
|
||||
): { parameterName: string; parameterValue: Expr } | Error {
|
||||
if (parameters.type === EdgeTreatmentType.Fillet) {
|
||||
const parameterValue =
|
||||
'variableName' in parameters.radius
|
||||
? parameters.radius.variableIdentifierAst
|
||||
: parameters.radius.valueAst
|
||||
return { parameterName: 'radius', parameterValue }
|
||||
} else if (parameters.type === EdgeTreatmentType.Chamfer) {
|
||||
const parameterValue =
|
||||
'variableName' in parameters.length
|
||||
? parameters.length.variableIdentifierAst
|
||||
: parameters.length.valueAst
|
||||
return { parameterName: 'length', parameterValue }
|
||||
} else {
|
||||
return new Error('Unsupported edge treatment type}')
|
||||
}
|
||||
}
|
||||
|
||||
// Type Guards
|
||||
function isEdgeTreatmentType(name: string): name is EdgeTreatmentType {
|
||||
return name === EdgeTreatmentType.Chamfer || name === EdgeTreatmentType.Fillet
|
||||
}
|
||||
function isEdgeType(name: string): name is EdgeTypes {
|
||||
return (
|
||||
name === 'getNextAdjacentEdge' ||
|
||||
name === 'getPreviousAdjacentEdge' ||
|
||||
name === 'getOppositeEdge'
|
||||
)
|
||||
return pathToFilletObj
|
||||
}
|
||||
|
||||
// Button states
|
||||
export const hasValidEdgeTreatmentSelection = ({
|
||||
|
||||
export const hasValidFilletSelection = ({
|
||||
selectionRanges,
|
||||
ast,
|
||||
code,
|
||||
@ -530,14 +433,11 @@ export const hasValidEdgeTreatmentSelection = ({
|
||||
ast: Node<Program>
|
||||
code: string
|
||||
}) => {
|
||||
// check if there is anything valid for the edge treatment in the scene
|
||||
// check if there is anything filletable in the scene
|
||||
let extrudeExists = false
|
||||
traverse(ast, {
|
||||
enter(node) {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
(node.callee.name === 'extrude' || node.callee.name === 'revolve')
|
||||
) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'extrude') {
|
||||
extrudeExists = true
|
||||
}
|
||||
},
|
||||
@ -594,39 +494,32 @@ export const hasValidEdgeTreatmentSelection = ({
|
||||
},
|
||||
})
|
||||
|
||||
// check if tag is used in edge treatment
|
||||
// check if tag is used in fillet
|
||||
if (tagExists && selection.artifact) {
|
||||
// create tag call
|
||||
let tagCall: Expr = getEdgeTagCall(tag, selection.artifact)
|
||||
|
||||
// check if tag is used in edge treatment
|
||||
let inEdgeTreatment = false
|
||||
let tagUsedInEdgeTreatment = false
|
||||
|
||||
// check if tag is used in fillet
|
||||
let inFillet = false
|
||||
let tagUsedInFillet = false
|
||||
traverse(ast, {
|
||||
enter(node) {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
}
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (hasTag(node, tagCall)) {
|
||||
tagUsedInEdgeTreatment = true
|
||||
tagUsedInFillet = true
|
||||
}
|
||||
}
|
||||
},
|
||||
leave(node) {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
}
|
||||
},
|
||||
})
|
||||
if (tagUsedInEdgeTreatment) {
|
||||
if (tagUsedInFillet) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -640,7 +533,7 @@ type EdgeTypes =
|
||||
| 'getPreviousAdjacentEdge'
|
||||
| 'getOppositeEdge'
|
||||
|
||||
export const isTagUsedInEdgeTreatment = ({
|
||||
export const isTagUsedInFillet = ({
|
||||
ast,
|
||||
callExp,
|
||||
}: {
|
||||
@ -650,21 +543,16 @@ export const isTagUsedInEdgeTreatment = ({
|
||||
const tag = getTagFromCallExpression(callExp)
|
||||
if (err(tag)) return []
|
||||
|
||||
let inEdgeTreatment = false
|
||||
let inFillet = false
|
||||
let inObj = false
|
||||
let inTagHelper: EdgeTypes | '' = ''
|
||||
const edges: Array<EdgeTypes> = []
|
||||
|
||||
traverse(ast, {
|
||||
enter: (node) => {
|
||||
// Check if we are entering an edge treatment call
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
}
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
node.properties.forEach((prop) => {
|
||||
if (
|
||||
prop.key.name === 'tags' &&
|
||||
@ -676,15 +564,17 @@ export const isTagUsedInEdgeTreatment = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inEdgeTreatment &&
|
||||
inFillet &&
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeType(node.callee.name)
|
||||
(node.callee.name === 'getOppositeEdge' ||
|
||||
node.callee.name === 'getNextAdjacentEdge' ||
|
||||
node.callee.name === 'getPreviousAdjacentEdge')
|
||||
) {
|
||||
inTagHelper = node.callee.name
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inEdgeTreatment &&
|
||||
inFillet &&
|
||||
!inTagHelper &&
|
||||
node.type === 'Identifier' &&
|
||||
node.name === tag
|
||||
@ -693,7 +583,7 @@ export const isTagUsedInEdgeTreatment = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inEdgeTreatment &&
|
||||
inFillet &&
|
||||
inTagHelper &&
|
||||
node.type === 'Identifier' &&
|
||||
node.name === tag
|
||||
@ -702,13 +592,10 @@ export const isTagUsedInEdgeTreatment = ({
|
||||
}
|
||||
},
|
||||
leave: (node) => {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
}
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
node.properties.forEach((prop) => {
|
||||
if (
|
||||
prop.key.name === 'tags' &&
|
||||
@ -720,9 +607,11 @@ export const isTagUsedInEdgeTreatment = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inEdgeTreatment &&
|
||||
inFillet &&
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeType(node.callee.name)
|
||||
(node.callee.name === 'getOppositeEdge' ||
|
||||
node.callee.name === 'getNextAdjacentEdge' ||
|
||||
node.callee.name === 'getPreviousAdjacentEdge')
|
||||
) {
|
||||
inTagHelper = ''
|
||||
}
|
@ -628,18 +628,6 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
||||
const extrudable = doesSceneHaveSweepableSketch(ast)
|
||||
expect(extrudable).toBeTruthy()
|
||||
})
|
||||
it('finds sketch001 and sketch002 pipes to be lofted', async () => {
|
||||
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||
plane001 = offsetPlane('XZ', 2)
|
||||
sketch002 = startSketchOn(plane001)
|
||||
|> circle({ center = [0, 0], radius = 3 }, %)
|
||||
`
|
||||
const ast = parse(exampleCode)
|
||||
if (err(ast)) throw ast
|
||||
const extrudable = doesSceneHaveSweepableSketch(ast, 2)
|
||||
expect(extrudable).toBeTruthy()
|
||||
})
|
||||
it('find sketch002 NOT pipe to be extruded', async () => {
|
||||
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([3.29, 7.86], %)
|
||||
|
@ -975,9 +975,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.type === 'Identifier' &&
|
||||
(node.callee.name === 'extrude' ||
|
||||
node.callee.name === 'revolve' ||
|
||||
node.callee.name === 'loft') &&
|
||||
(node.callee.name === 'extrude' || node.callee.name === 'revolve') &&
|
||||
node.arguments?.[1]?.type === 'Identifier' &&
|
||||
node.arguments[1].name === varDec.id.name
|
||||
) {
|
||||
@ -990,7 +988,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
||||
}
|
||||
|
||||
/** File must contain at least one sketch that has not been extruded already */
|
||||
export function doesSceneHaveSweepableSketch(ast: Node<Program>, count = 1) {
|
||||
export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
||||
const theMap: any = {}
|
||||
traverse(ast as any, {
|
||||
enter(node) {
|
||||
@ -1039,7 +1037,7 @@ export function doesSceneHaveSweepableSketch(ast: Node<Program>, count = 1) {
|
||||
}
|
||||
},
|
||||
})
|
||||
return Object.keys(theMap).length >= count
|
||||
return Object.keys(theMap).length > 0
|
||||
}
|
||||
|
||||
export function getObjExprProperty(
|
||||
|
@ -63,7 +63,7 @@ log(5, myVar)
|
||||
})
|
||||
it('function declaration with call', () => {
|
||||
const code = [
|
||||
'fn funcN(a, b) {',
|
||||
'fn funcN = (a, b) => {',
|
||||
' return a + b',
|
||||
'}',
|
||||
'theVar = 60',
|
||||
@ -101,7 +101,7 @@ log(5, myVar)
|
||||
})
|
||||
it('recast BinaryExpression piped into CallExpression', () => {
|
||||
const code = [
|
||||
'fn myFn(a) {',
|
||||
'fn myFn = (a) => {',
|
||||
' return a + 1',
|
||||
'}',
|
||||
'myVar = 5 + 1',
|
||||
@ -245,7 +245,7 @@ key = 'c'
|
||||
expect(recasted).toBe(code)
|
||||
})
|
||||
it('comments in a fn block', () => {
|
||||
const code = `fn myFn() {
|
||||
const code = `fn myFn = () => {
|
||||
// this is a comment
|
||||
yo = { a = { b = { c = '123' } } }
|
||||
|
||||
|
@ -11,8 +11,8 @@ Map {
|
||||
],
|
||||
],
|
||||
"range": [
|
||||
12,
|
||||
31,
|
||||
37,
|
||||
64,
|
||||
0,
|
||||
],
|
||||
},
|
||||
|
382
src/lang/tokeniser.test.ts
Normal file
@ -0,0 +1,382 @@
|
||||
import { lexer, initPromise } from './wasm'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('testing lexer', () => {
|
||||
it('async lexer works too', async () => {
|
||||
const code = '1 + 2'
|
||||
const code2 = `const yo = {key: 'value'}`
|
||||
const code3 = `const yo = 45 /* this is a comment
|
||||
const ya = 6 */
|
||||
const yi=45`
|
||||
expect(lexer(code)).toEqual(lexer(code))
|
||||
expect(lexer(code2)).toEqual(lexer(code2))
|
||||
expect(lexer(code3)).toEqual(lexer(code3))
|
||||
})
|
||||
it('test lexer', () => {
|
||||
expect(stringSummaryLexer('1 + 2')).toEqual([
|
||||
"number '1' from 0 to 1",
|
||||
"whitespace ' ' from 1 to 3",
|
||||
"operator '+' from 3 to 4",
|
||||
"whitespace ' ' from 4 to 5",
|
||||
"number '2' from 5 to 6",
|
||||
])
|
||||
expect(stringSummaryLexer('54 + 22500 + 6')).toEqual([
|
||||
"number '54' from 0 to 2",
|
||||
"whitespace ' ' from 2 to 3",
|
||||
"operator '+' from 3 to 4",
|
||||
"whitespace ' ' from 4 to 5",
|
||||
"number '22500' from 5 to 10",
|
||||
"whitespace ' ' from 10 to 11",
|
||||
"operator '+' from 11 to 12",
|
||||
"whitespace ' ' from 12 to 13",
|
||||
"number '6' from 13 to 14",
|
||||
])
|
||||
expect(stringSummaryLexer('a + bo + t5 - 6')).toEqual([
|
||||
"word 'a' from 0 to 1",
|
||||
"whitespace ' ' from 1 to 2",
|
||||
"operator '+' from 2 to 3",
|
||||
"whitespace ' ' from 3 to 4",
|
||||
"word 'bo' from 4 to 6",
|
||||
"whitespace ' ' from 6 to 7",
|
||||
"operator '+' from 7 to 8",
|
||||
"whitespace ' ' from 8 to 9",
|
||||
"word 't5' from 9 to 11",
|
||||
"whitespace ' ' from 11 to 12",
|
||||
"operator '-' from 12 to 13",
|
||||
"whitespace ' ' from 13 to 14",
|
||||
"number '6' from 14 to 15",
|
||||
])
|
||||
expect(stringSummaryLexer('a + "a str" - 6')).toEqual([
|
||||
"word 'a' from 0 to 1",
|
||||
"whitespace ' ' from 1 to 2",
|
||||
"operator '+' from 2 to 3",
|
||||
"whitespace ' ' from 3 to 4",
|
||||
'string \'"a str"\' from 4 to 11',
|
||||
"whitespace ' ' from 11 to 12",
|
||||
"operator '-' from 12 to 13",
|
||||
"whitespace ' ' from 13 to 14",
|
||||
"number '6' from 14 to 15",
|
||||
])
|
||||
expect(stringSummaryLexer("a + 'str'")).toEqual([
|
||||
"word 'a' from 0 to 1",
|
||||
"whitespace ' ' from 1 to 2",
|
||||
"operator '+' from 2 to 3",
|
||||
"whitespace ' ' from 3 to 4",
|
||||
"string ''str'' from 4 to 9",
|
||||
])
|
||||
expect(stringSummaryLexer("a +'str'")).toEqual([
|
||||
"word 'a' from 0 to 1",
|
||||
"whitespace ' ' from 1 to 2",
|
||||
"operator '+' from 2 to 3",
|
||||
"string ''str'' from 3 to 8",
|
||||
])
|
||||
|
||||
expect(stringSummaryLexer('a + (sick)')).toEqual([
|
||||
"word 'a' from 0 to 1",
|
||||
"whitespace ' ' from 1 to 2",
|
||||
"operator '+' from 2 to 3",
|
||||
"whitespace ' ' from 3 to 4",
|
||||
"brace '(' from 4 to 5",
|
||||
"word 'sick' from 5 to 9",
|
||||
"brace ')' from 9 to 10",
|
||||
])
|
||||
|
||||
expect(stringSummaryLexer('a + { sick}')).toEqual([
|
||||
"word 'a' from 0 to 1",
|
||||
"whitespace ' ' from 1 to 2",
|
||||
"operator '+' from 2 to 3",
|
||||
"whitespace ' ' from 3 to 4",
|
||||
"brace '{' from 4 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"word 'sick' from 6 to 10",
|
||||
"brace '}' from 10 to 11",
|
||||
])
|
||||
|
||||
expect(stringSummaryLexer("log('hi')")).toEqual([
|
||||
"word 'log' from 0 to 3",
|
||||
"brace '(' from 3 to 4",
|
||||
"string ''hi'' from 4 to 8",
|
||||
"brace ')' from 8 to 9",
|
||||
])
|
||||
expect(stringSummaryLexer("log('hi', 'hello')")).toEqual([
|
||||
"word 'log' from 0 to 3",
|
||||
"brace '(' from 3 to 4",
|
||||
"string ''hi'' from 4 to 8",
|
||||
"comma ',' from 8 to 9",
|
||||
"whitespace ' ' from 9 to 10",
|
||||
"string ''hello'' from 10 to 17",
|
||||
"brace ')' from 17 to 18",
|
||||
])
|
||||
expect(stringSummaryLexer('fn funcName = (param1, param2) => {}')).toEqual([
|
||||
"keyword 'fn' from 0 to 2",
|
||||
"whitespace ' ' from 2 to 3",
|
||||
"word 'funcName' from 3 to 11",
|
||||
"whitespace ' ' from 11 to 12",
|
||||
"operator '=' from 12 to 13",
|
||||
"whitespace ' ' from 13 to 14",
|
||||
"brace '(' from 14 to 15",
|
||||
"word 'param1' from 15 to 21",
|
||||
"comma ',' from 21 to 22",
|
||||
"whitespace ' ' from 22 to 23",
|
||||
"word 'param2' from 23 to 29",
|
||||
"brace ')' from 29 to 30",
|
||||
"whitespace ' ' from 30 to 31",
|
||||
"operator '=>' from 31 to 33",
|
||||
"whitespace ' ' from 33 to 34",
|
||||
"brace '{' from 34 to 35",
|
||||
"brace '}' from 35 to 36",
|
||||
])
|
||||
})
|
||||
it('test negative and decimal numbers', () => {
|
||||
expect(stringSummaryLexer('-1')).toEqual([
|
||||
"operator '-' from 0 to 1",
|
||||
"number '1' from 1 to 2",
|
||||
])
|
||||
expect(stringSummaryLexer('-1.5')).toEqual([
|
||||
"operator '-' from 0 to 1",
|
||||
"number '1.5' from 1 to 4",
|
||||
])
|
||||
expect(stringSummaryLexer('1.5')).toEqual([
|
||||
"number '1.5' from 0 to 3",
|
||||
])
|
||||
expect(stringSummaryLexer('1.5 + 2.5')).toEqual([
|
||||
"number '1.5' from 0 to 3",
|
||||
"whitespace ' ' from 3 to 4",
|
||||
"operator '+' from 4 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"number '2.5' from 6 to 9",
|
||||
])
|
||||
expect(stringSummaryLexer('1.5 - 2.5')).toEqual([
|
||||
"number '1.5' from 0 to 3",
|
||||
"whitespace ' ' from 3 to 4",
|
||||
"operator '-' from 4 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"number '2.5' from 6 to 9",
|
||||
])
|
||||
expect(stringSummaryLexer('1.5 + -2.5')).toEqual([
|
||||
"number '1.5' from 0 to 3",
|
||||
"whitespace ' ' from 3 to 4",
|
||||
"operator '+' from 4 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"operator '-' from 6 to 7",
|
||||
"number '2.5' from 7 to 10",
|
||||
])
|
||||
expect(stringSummaryLexer('-1.5 + 2.5')).toEqual([
|
||||
"operator '-' from 0 to 1",
|
||||
"number '1.5' from 1 to 4",
|
||||
"whitespace ' ' from 4 to 5",
|
||||
"operator '+' from 5 to 6",
|
||||
"whitespace ' ' from 6 to 7",
|
||||
"number '2.5' from 7 to 10",
|
||||
])
|
||||
})
|
||||
it('testing piping operator', () => {
|
||||
const result = stringSummaryLexer(`sketch mySketch {
|
||||
lineTo(2, 3)
|
||||
} |> rx(45, %)`)
|
||||
expect(result).toEqual([
|
||||
"type 'sketch' from 0 to 6",
|
||||
"whitespace ' ' from 6 to 7",
|
||||
"word 'mySketch' from 7 to 15",
|
||||
"whitespace ' ' from 15 to 16",
|
||||
"brace '{' from 16 to 17",
|
||||
"whitespace '\n ' from 17 to 24",
|
||||
"word 'lineTo' from 24 to 30",
|
||||
"brace '(' from 30 to 31",
|
||||
"number '2' from 31 to 32",
|
||||
"comma ',' from 32 to 33",
|
||||
"whitespace ' ' from 33 to 34",
|
||||
"number '3' from 34 to 35",
|
||||
"brace ')' from 35 to 36",
|
||||
"whitespace '\n ' from 36 to 41",
|
||||
"brace '}' from 41 to 42",
|
||||
"whitespace ' ' from 42 to 43",
|
||||
"operator '|>' from 43 to 45",
|
||||
"whitespace ' ' from 45 to 46",
|
||||
"word 'rx' from 46 to 48",
|
||||
"brace '(' from 48 to 49",
|
||||
"number '45' from 49 to 51",
|
||||
"comma ',' from 51 to 52",
|
||||
"whitespace ' ' from 52 to 53",
|
||||
"operator '%' from 53 to 54",
|
||||
"brace ')' from 54 to 55",
|
||||
])
|
||||
})
|
||||
it('testing array declaration', () => {
|
||||
const result = stringSummaryLexer(`const yo = [1, 2]`)
|
||||
expect(result).toEqual([
|
||||
"keyword 'const' from 0 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"word 'yo' from 6 to 8",
|
||||
"whitespace ' ' from 8 to 9",
|
||||
"operator '=' from 9 to 10",
|
||||
"whitespace ' ' from 10 to 11",
|
||||
"brace '[' from 11 to 12",
|
||||
"number '1' from 12 to 13",
|
||||
"comma ',' from 13 to 14",
|
||||
"whitespace ' ' from 14 to 15",
|
||||
"number '2' from 15 to 16",
|
||||
"brace ']' from 16 to 17",
|
||||
])
|
||||
})
|
||||
it('testing object declaration', () => {
|
||||
const result = stringSummaryLexer(`const yo = {key: 'value'}`)
|
||||
expect(result).toEqual([
|
||||
"keyword 'const' from 0 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"word 'yo' from 6 to 8",
|
||||
"whitespace ' ' from 8 to 9",
|
||||
"operator '=' from 9 to 10",
|
||||
"whitespace ' ' from 10 to 11",
|
||||
"brace '{' from 11 to 12",
|
||||
"word 'key' from 12 to 15",
|
||||
"colon ':' from 15 to 16",
|
||||
"whitespace ' ' from 16 to 17",
|
||||
"string ''value'' from 17 to 24",
|
||||
"brace '}' from 24 to 25",
|
||||
])
|
||||
})
|
||||
it('testing object property access', () => {
|
||||
const result = stringSummaryLexer(`const yo = {key: 'value'}
|
||||
const prop = yo.key
|
||||
const prop2 = yo['key']
|
||||
const key = 'key'
|
||||
const prop3 = yo[key]`)
|
||||
expect(result).toEqual([
|
||||
"keyword 'const' from 0 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"word 'yo' from 6 to 8",
|
||||
"whitespace ' ' from 8 to 9",
|
||||
"operator '=' from 9 to 10",
|
||||
"whitespace ' ' from 10 to 11",
|
||||
"brace '{' from 11 to 12",
|
||||
"word 'key' from 12 to 15",
|
||||
"colon ':' from 15 to 16",
|
||||
"whitespace ' ' from 16 to 17",
|
||||
"string ''value'' from 17 to 24",
|
||||
"brace '}' from 24 to 25",
|
||||
"whitespace '\n' from 25 to 26",
|
||||
"keyword 'const' from 26 to 31",
|
||||
"whitespace ' ' from 31 to 32",
|
||||
"word 'prop' from 32 to 36",
|
||||
"whitespace ' ' from 36 to 37",
|
||||
"operator '=' from 37 to 38",
|
||||
"whitespace ' ' from 38 to 39",
|
||||
"word 'yo' from 39 to 41",
|
||||
"period '.' from 41 to 42",
|
||||
"word 'key' from 42 to 45",
|
||||
"whitespace '\n' from 45 to 46",
|
||||
"keyword 'const' from 46 to 51",
|
||||
"whitespace ' ' from 51 to 52",
|
||||
"word 'prop2' from 52 to 57",
|
||||
"whitespace ' ' from 57 to 58",
|
||||
"operator '=' from 58 to 59",
|
||||
"whitespace ' ' from 59 to 60",
|
||||
"word 'yo' from 60 to 62",
|
||||
"brace '[' from 62 to 63",
|
||||
"string ''key'' from 63 to 68",
|
||||
"brace ']' from 68 to 69",
|
||||
"whitespace '\n' from 69 to 70",
|
||||
"keyword 'const' from 70 to 75",
|
||||
"whitespace ' ' from 75 to 76",
|
||||
"word 'key' from 76 to 79",
|
||||
"whitespace ' ' from 79 to 80",
|
||||
"operator '=' from 80 to 81",
|
||||
"whitespace ' ' from 81 to 82",
|
||||
"string ''key'' from 82 to 87",
|
||||
"whitespace '\n' from 87 to 88",
|
||||
"keyword 'const' from 88 to 93",
|
||||
"whitespace ' ' from 93 to 94",
|
||||
"word 'prop3' from 94 to 99",
|
||||
"whitespace ' ' from 99 to 100",
|
||||
"operator '=' from 100 to 101",
|
||||
"whitespace ' ' from 101 to 102",
|
||||
"word 'yo' from 102 to 104",
|
||||
"brace '[' from 104 to 105",
|
||||
"word 'key' from 105 to 108",
|
||||
"brace ']' from 108 to 109",
|
||||
])
|
||||
})
|
||||
it('testing tokenising line comments', () => {
|
||||
const result = stringSummaryLexer(`const yo = 45 // this is a comment
|
||||
const yo = 6`)
|
||||
expect(result).toEqual([
|
||||
"keyword 'const' from 0 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"word 'yo' from 6 to 8",
|
||||
"whitespace ' ' from 8 to 9",
|
||||
"operator '=' from 9 to 10",
|
||||
"whitespace ' ' from 10 to 11",
|
||||
"number '45' from 11 to 13",
|
||||
"whitespace ' ' from 13 to 14",
|
||||
"lineComment '// this is a comment' from 14 to 34",
|
||||
"whitespace '\n' from 34 to 35",
|
||||
"keyword 'const' from 35 to 40",
|
||||
"whitespace ' ' from 40 to 41",
|
||||
"word 'yo' from 41 to 43",
|
||||
"whitespace ' ' from 43 to 44",
|
||||
"operator '=' from 44 to 45",
|
||||
"whitespace ' ' from 45 to 46",
|
||||
"number '6' from 46 to 47",
|
||||
])
|
||||
})
|
||||
it('testing tokenising line comments by itself', () => {
|
||||
const result = stringSummaryLexer(`log('hi')
|
||||
// comment on a line by itself
|
||||
const yo=45`)
|
||||
expect(result).toEqual([
|
||||
"word 'log' from 0 to 3",
|
||||
"brace '(' from 3 to 4",
|
||||
"string ''hi'' from 4 to 8",
|
||||
"brace ')' from 8 to 9",
|
||||
"whitespace '\n' from 9 to 10",
|
||||
"lineComment '// comment on a line by itself' from 10 to 40",
|
||||
"whitespace '\n' from 40 to 41",
|
||||
"keyword 'const' from 41 to 46",
|
||||
"whitespace ' ' from 46 to 47",
|
||||
"word 'yo' from 47 to 49",
|
||||
"operator '=' from 49 to 50",
|
||||
"number '45' from 50 to 52",
|
||||
])
|
||||
})
|
||||
it('testing tokenising block comments', () => {
|
||||
const result = stringSummaryLexer(`const yo = 45 /* this is a comment
|
||||
const ya = 6 */
|
||||
const yi=45`)
|
||||
expect(result).toEqual([
|
||||
"keyword 'const' from 0 to 5",
|
||||
"whitespace ' ' from 5 to 6",
|
||||
"word 'yo' from 6 to 8",
|
||||
"whitespace ' ' from 8 to 9",
|
||||
"operator '=' from 9 to 10",
|
||||
"whitespace ' ' from 10 to 11",
|
||||
"number '45' from 11 to 13",
|
||||
"whitespace ' ' from 13 to 14",
|
||||
`blockComment '/* this is a comment
|
||||
const ya = 6 */' from 14 to 50`,
|
||||
"whitespace '\n' from 50 to 51",
|
||||
"keyword 'const' from 51 to 56",
|
||||
"whitespace ' ' from 56 to 57",
|
||||
"word 'yi' from 57 to 59",
|
||||
"operator '=' from 59 to 60",
|
||||
"number '45' from 60 to 62",
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
// helpers
|
||||
|
||||
const stringSummaryLexer = (input: string) => {
|
||||
const tokens = lexer(input)
|
||||
if (err(tokens)) return []
|
||||
return tokens.map(
|
||||
({ type, value, start, end }) =>
|
||||
`${type.padEnd(12, ' ')} ${`'${value}'`.padEnd(10, ' ')} from ${String(
|
||||
start
|
||||
).padEnd(3, ' ')} to ${end}`
|
||||
)
|
||||
}
|
@ -3,6 +3,7 @@ import init, {
|
||||
recast_wasm,
|
||||
execute_wasm,
|
||||
kcl_lint,
|
||||
lexer_wasm,
|
||||
modify_ast_for_sketch_wasm,
|
||||
is_points_ccw,
|
||||
get_tangential_arc_to_info,
|
||||
@ -23,6 +24,7 @@ import { EngineCommandManager } from './std/engineConnection'
|
||||
import { Discovered } from '../wasm-lib/kcl/bindings/Discovered'
|
||||
import { KclValue } from '../wasm-lib/kcl/bindings/KclValue'
|
||||
import type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
import type { Token } from '../wasm-lib/kcl/bindings/Token'
|
||||
import { Coords2d } from './std/sketch'
|
||||
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
||||
import { CoreDumpInfo } from 'wasm-lib/kcl/bindings/CoreDumpInfo'
|
||||
@ -505,6 +507,10 @@ export const modifyGrid = async (
|
||||
}
|
||||
}
|
||||
|
||||
export function lexer(str: string): Token[] | Error {
|
||||
return lexer_wasm(str)
|
||||
}
|
||||
|
||||
export const modifyAstForSketch = async (
|
||||
engineCommandManager: EngineCommandManager,
|
||||
ast: Node<Program>,
|
||||
|
@ -31,9 +31,6 @@ export type ModelingCommandSchema = {
|
||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||
distance: KclCommandValue
|
||||
}
|
||||
Loft: {
|
||||
selection: Selections
|
||||
}
|
||||
Revolve: {
|
||||
selection: Selections
|
||||
angle: KclCommandValue
|
||||
@ -263,20 +260,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
},
|
||||
},
|
||||
},
|
||||
Loft: {
|
||||
description: 'Create a 3D body by blending between two or more sketches',
|
||||
icon: 'loft',
|
||||
needsReview: true,
|
||||
args: {
|
||||
selection: {
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['solid2D'],
|
||||
multiple: true,
|
||||
required: true,
|
||||
skip: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
|
||||
Revolve: {
|
||||
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||
|
@ -52,7 +52,6 @@ export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
||||
export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||
SKETCH: 'sketch',
|
||||
EXTRUDE: 'extrude',
|
||||
LOFT: 'loft',
|
||||
SEGMENT: 'seg',
|
||||
REVOLVE: 'revolve',
|
||||
PLANE: 'plane',
|
||||
|
@ -46,21 +46,9 @@ describe('desktop utilities', () => {
|
||||
'project-without-kcl-files',
|
||||
'another-valid-project',
|
||||
],
|
||||
'/test/projects/valid-project': [
|
||||
'file1.kcl',
|
||||
'file2.stp',
|
||||
'file3.kcl',
|
||||
'directory1',
|
||||
],
|
||||
'/test/projects/valid-project/directory1': [],
|
||||
'/test/projects/valid-project': ['file1.kcl', 'file2.stp'],
|
||||
'/test/projects/project-without-kcl-files': ['file3.glb'],
|
||||
'/test/projects/another-valid-project': [
|
||||
'file4.kcl',
|
||||
'directory2',
|
||||
'directory3',
|
||||
],
|
||||
'/test/projects/another-valid-project/directory2': [],
|
||||
'/test/projects/another-valid-project/directory3': [],
|
||||
'/test/projects/another-valid-project': ['file4.kcl'],
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
@ -131,15 +119,6 @@ describe('desktop utilities', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('correctly counts directories and files', async () => {
|
||||
const projects = await listProjects(mockConfig)
|
||||
// Verify that directories and files are counted correctly
|
||||
expect(projects[0].directory_count).toEqual(1)
|
||||
expect(projects[0].kcl_file_count).toEqual(2)
|
||||
expect(projects[1].directory_count).toEqual(2)
|
||||
expect(projects[1].kcl_file_count).toEqual(1)
|
||||
})
|
||||
|
||||
it('handles empty project directory', async () => {
|
||||
// Adjust mockFileSystem to simulate empty directory
|
||||
mockFileSystem['/test/projects'] = []
|
||||
|
@ -307,10 +307,7 @@ const directoryCount = (file: FileEntry) => {
|
||||
let count = 0
|
||||
if (file.children) {
|
||||
for (let entry of file.children) {
|
||||
// We only want to count FileEntries with children, e.g. folders
|
||||
if (entry.children !== null) {
|
||||
count += 1
|
||||
}
|
||||
directoryCount(entry)
|
||||
}
|
||||
}
|
||||
|
@ -529,10 +529,6 @@ function nodeHasExtrude(node: CommonASTNode) {
|
||||
doesPipeHaveCallExp({
|
||||
calleeName: 'revolve',
|
||||
...node,
|
||||
}) ||
|
||||
doesPipeHaveCallExp({
|
||||
calleeName: 'loft',
|
||||
...node,
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -563,22 +559,6 @@ export function canSweepSelection(selection: Selections) {
|
||||
)
|
||||
}
|
||||
|
||||
export function canLoftSelection(selection: Selections) {
|
||||
const commonNodes = selection.graphSelections.map((_, i) =>
|
||||
buildCommonNodeFromSelection(selection, i)
|
||||
)
|
||||
return (
|
||||
!!isCursorInSketchCommandRange(
|
||||
engineCommandManager.artifactGraph,
|
||||
selection
|
||||
) &&
|
||||
commonNodes.length > 1 &&
|
||||
commonNodes.every((n) => !hasSketchPipeBeenExtruded(n.selection, n.ast)) &&
|
||||
commonNodes.every((n) => nodeHasClose(n) || nodeHasCircle(n)) &&
|
||||
commonNodes.every((n) => !nodeHasExtrude(n))
|
||||
)
|
||||
}
|
||||
|
||||
// This accounts for non-geometry selections under "other"
|
||||
export type ResolvedSelectionType = Artifact['type'] | 'other'
|
||||
export type SelectionCountsByType = Map<ResolvedSelectionType, number>
|
||||
|
@ -139,14 +139,9 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
||||
},
|
||||
{
|
||||
id: 'loft',
|
||||
onClick: ({ commandBarSend }) =>
|
||||
commandBarSend({
|
||||
type: 'Find and select command',
|
||||
data: { name: 'Loft', groupId: 'modeling' },
|
||||
}),
|
||||
disabled: (state) => !state.can({ type: 'Loft' }),
|
||||
onClick: () => console.error('Loft not yet implemented'),
|
||||
icon: 'loft',
|
||||
status: 'available',
|
||||
status: 'kcl-only',
|
||||
title: 'Loft',
|
||||
hotkey: 'L',
|
||||
description:
|
||||
|
@ -44,14 +44,9 @@ import {
|
||||
addOffsetPlane,
|
||||
deleteFromSelection,
|
||||
extrudeSketch,
|
||||
loftSketches,
|
||||
revolveSketch,
|
||||
} from 'lang/modifyAst'
|
||||
import {
|
||||
applyEdgeTreatmentToSelection,
|
||||
EdgeTreatmentType,
|
||||
FilletParameters,
|
||||
} from 'lang/modifyAst/addEdgeTreatment'
|
||||
import { applyFilletToSelection } from 'lang/modifyAst/addFillet'
|
||||
import { getNodeFromPath } from '../lang/queryAst'
|
||||
import {
|
||||
applyConstraintEqualAngle,
|
||||
@ -257,7 +252,6 @@ export type ModelingMachineEvent =
|
||||
| { type: 'Export'; data: ModelingCommandSchema['Export'] }
|
||||
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
|
||||
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
|
||||
| { type: 'Loft'; data?: ModelingCommandSchema['Loft'] }
|
||||
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
|
||||
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
|
||||
| { type: 'Offset plane'; data: ModelingCommandSchema['Offset plane'] }
|
||||
@ -389,8 +383,7 @@ export const modelingMachine = setup({
|
||||
guards: {
|
||||
'Selection is on face': () => false,
|
||||
'has valid sweep selection': () => false,
|
||||
'has valid loft selection': () => false,
|
||||
'has valid edge treatment selection': () => false,
|
||||
'has valid fillet selection': () => false,
|
||||
'Has exportable geometry': () => false,
|
||||
'has valid selection for deletion': () => false,
|
||||
'has made first point': ({ context }) => {
|
||||
@ -746,19 +739,14 @@ export const modelingMachine = setup({
|
||||
// Extract inputs
|
||||
const ast = kclManager.ast
|
||||
const { selection, radius } = event.data
|
||||
const parameters: FilletParameters = {
|
||||
type: EdgeTreatmentType.Fillet,
|
||||
radius,
|
||||
}
|
||||
|
||||
// Apply fillet to selection
|
||||
const applyEdgeTreatmentToSelectionResult = applyEdgeTreatmentToSelection(
|
||||
const applyFilletToSelectionResult = applyFilletToSelection(
|
||||
ast,
|
||||
selection,
|
||||
parameters
|
||||
radius
|
||||
)
|
||||
if (err(applyEdgeTreatmentToSelectionResult))
|
||||
return applyEdgeTreatmentToSelectionResult
|
||||
if (err(applyFilletToSelectionResult)) return applyFilletToSelectionResult
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||
@ -1532,50 +1520,6 @@ export const modelingMachine = setup({
|
||||
updateAstResult.newAst
|
||||
)
|
||||
|
||||
if (updateAstResult?.selections) {
|
||||
editorManager.selectRange(updateAstResult?.selections)
|
||||
}
|
||||
}
|
||||
),
|
||||
loftAstMod: fromPromise(
|
||||
async ({
|
||||
input,
|
||||
}: {
|
||||
input: ModelingCommandSchema['Loft'] | undefined
|
||||
}) => {
|
||||
if (!input) return new Error('No input provided')
|
||||
// Extract inputs
|
||||
const ast = kclManager.ast
|
||||
const { selection } = input
|
||||
const declarators = selection.graphSelections.flatMap((s) => {
|
||||
const path = getNodePathFromSourceRange(ast, s?.codeRef.range)
|
||||
const nodeFromPath = getNodeFromPath<VariableDeclarator>(
|
||||
ast,
|
||||
path,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
return err(nodeFromPath) ? [] : nodeFromPath.node
|
||||
})
|
||||
|
||||
// TODO: add better validation on selection
|
||||
if (!(declarators && declarators.length > 1)) {
|
||||
trap('Not enough sketches selected')
|
||||
}
|
||||
|
||||
// Perform the loft
|
||||
const loftSketchesRes = loftSketches(ast, declarators)
|
||||
const updateAstResult = await kclManager.updateAst(
|
||||
loftSketchesRes.modifiedAst,
|
||||
true,
|
||||
{
|
||||
focusPath: [loftSketchesRes.pathToNode],
|
||||
}
|
||||
)
|
||||
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(
|
||||
updateAstResult.newAst
|
||||
)
|
||||
|
||||
if (updateAstResult?.selections) {
|
||||
editorManager.selectRange(updateAstResult?.selections)
|
||||
}
|
||||
@ -1617,14 +1561,9 @@ export const modelingMachine = setup({
|
||||
reenter: false,
|
||||
},
|
||||
|
||||
Loft: {
|
||||
target: 'Applying loft',
|
||||
reenter: true,
|
||||
},
|
||||
|
||||
Fillet: {
|
||||
target: 'idle',
|
||||
guard: 'has valid edge treatment selection',
|
||||
guard: 'has valid fillet selection', // TODO: fix selections
|
||||
actions: ['AST fillet'],
|
||||
reenter: false,
|
||||
},
|
||||
@ -2370,19 +2309,6 @@ export const modelingMachine = setup({
|
||||
onError: ['idle'],
|
||||
},
|
||||
},
|
||||
|
||||
'Applying loft': {
|
||||
invoke: {
|
||||
src: 'loftAstMod',
|
||||
id: 'loftAstMod',
|
||||
input: ({ event }) => {
|
||||
if (event.type !== 'Loft') return undefined
|
||||
return event.data
|
||||
},
|
||||
onDone: ['idle'],
|
||||
onError: ['idle'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
initial: 'idle',
|
||||
|
87
src/wasm-lib/Cargo.lock
generated
@ -443,9 +443,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.21"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
|
||||
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -453,9 +453,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.21"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
|
||||
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -774,22 +774,6 @@ dependencies = [
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dhat"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98cd11d84628e233de0ce467de10b8633f4ddaecafadefc86e13b84b8739b827"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"lazy_static",
|
||||
"mintex",
|
||||
"parking_lot 0.12.3",
|
||||
"rustc-hash 1.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thousands",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
@ -1182,9 +1166,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@ -1589,7 +1573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
"hashbrown 0.15.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -1706,7 +1690,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.28"
|
||||
version = "0.2.26"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1721,7 +1705,6 @@ dependencies = [
|
||||
"dashmap 6.1.0",
|
||||
"databake",
|
||||
"derive-docs",
|
||||
"dhat",
|
||||
"expectorate",
|
||||
"fnv",
|
||||
"form_urlencoded",
|
||||
@ -1766,7 +1749,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"web-time",
|
||||
"winnow",
|
||||
"zip",
|
||||
]
|
||||
@ -1800,9 +1782,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.3.28"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "933cb5f77624386c87d296e3fd493daf50156d1cbfa03b9f333a6d4da2896369"
|
||||
checksum = "f6359cc0a1bbccbcf78775eea17a033cf2aa89d3fe6a9784f8ce94e5f882c185"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1831,7 +1813,7 @@ dependencies = [
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"thiserror 2.0.0",
|
||||
"thiserror 1.0.68",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
@ -2074,12 +2056,6 @@ version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
|
||||
|
||||
[[package]]
|
||||
name = "mintex"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.2"
|
||||
@ -2668,7 +2644,7 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 1.0.68",
|
||||
@ -2685,7 +2661,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"rand 0.8.5",
|
||||
"ring",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"slab",
|
||||
"thiserror 1.0.68",
|
||||
@ -2921,9 +2897,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest-conditional-middleware"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f67ad7fdf5c0a015763fcd164bee294b13fb7b6f89f1b55961d40f00c3e32d6b"
|
||||
checksum = "b1663d9d4fbb6e3900f91455d6d7833301c91ae3c7fc6e116fd7acd40e478a93"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"http 1.1.0",
|
||||
@ -2933,9 +2909,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest-middleware"
|
||||
version = "0.4.0"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3"
|
||||
checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -2948,9 +2924,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest-retry"
|
||||
version = "0.7.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c73e4195a6bfbcb174b790d9b3407ab90646976c55de58a6515da25d851178"
|
||||
checksum = "a83df1aaec00176d0fabb65dea13f832d2a446ca99107afc17c5d2d4981221d0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -2962,7 +2938,6 @@ dependencies = [
|
||||
"reqwest",
|
||||
"reqwest-middleware",
|
||||
"retry-policies",
|
||||
"thiserror 1.0.68",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"wasm-timer",
|
||||
@ -2970,9 +2945,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest-tracing"
|
||||
version = "0.5.4"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff82cf5730a1311fb9413b0bc2b8e743e0157cd73f010ab4ec374a923873b6a2"
|
||||
checksum = "bfdd9bfa64c72233d8dd99ab7883efcdefe9e16d46488ecb9228b71a2e2ceb45"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -3026,12 +3001,6 @@ version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.0.0"
|
||||
@ -3066,9 +3035,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.19"
|
||||
version = "0.23.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
|
||||
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
@ -3102,9 +3071,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.10.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
@ -3668,12 +3637,6 @@ dependencies = [
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thousands"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
|
@ -74,8 +74,8 @@ members = [
|
||||
|
||||
[workspace.dependencies]
|
||||
http = "1"
|
||||
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.77", features = ["websocket"] }
|
||||
kittycad = { version = "0.3.25", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.76", features = ["websocket"] }
|
||||
|
||||
[[test]]
|
||||
name = "executor"
|
||||
|
@ -171,7 +171,7 @@ fn do_stdlib_inner(
|
||||
code_blocks.iter().map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
}).collect::<Vec<String>>()
|
||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -150,7 +150,7 @@ impl crate::docs::StdLibFn for Show {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Show {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -150,7 +150,7 @@ impl crate::docs::StdLibFn for MyFunc {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -158,7 +158,7 @@ impl crate::docs::StdLibFn for LineTo {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -150,7 +150,7 @@ impl crate::docs::StdLibFn for Min {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Show {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Import {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Import {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Import {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Show {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -108,7 +108,7 @@ impl crate::docs::StdLibFn for SomeFunction {
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let program = crate::Program::parse(cb).unwrap();
|
||||
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.ast.recast(&options, 0)
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.28"
|
||||
version = "0.2.26"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -16,15 +16,11 @@ async-recursion = "1.1.1"
|
||||
async-trait = "0.1.83"
|
||||
base64 = "0.22.1"
|
||||
chrono = "0.4.38"
|
||||
clap = { version = "4.5.21", default-features = false, optional = true, features = [
|
||||
"std",
|
||||
"derive",
|
||||
] }
|
||||
clap = { version = "4.5.20", default-features = false, optional = true, features = ["std", "derive"] }
|
||||
convert_case = "0.6.0"
|
||||
dashmap = "6.1.0"
|
||||
databake = { version = "0.1.8", features = ["derive"] }
|
||||
derive-docs = { version = "0.1.29", path = "../derive-docs" }
|
||||
dhat = { version = "0.3", optional = true }
|
||||
fnv = "1.0.7"
|
||||
form_urlencoded = "1.2.1"
|
||||
futures = { version = "0.3.31" }
|
||||
@ -41,46 +37,27 @@ miette = "7.2.0"
|
||||
mime_guess = "2.0.5"
|
||||
parse-display = "0.9.1"
|
||||
pyo3 = { version = "0.22.6", optional = true }
|
||||
reqwest = { version = "0.12", default-features = false, features = [
|
||||
"stream",
|
||||
"rustls-tls",
|
||||
] }
|
||||
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
|
||||
ropey = "1.6.1"
|
||||
schemars = { version = "0.8.17", features = [
|
||||
"impl_json_schema",
|
||||
"indexmap2",
|
||||
"url",
|
||||
"uuid1",
|
||||
"preserve_order",
|
||||
] }
|
||||
schemars = { version = "0.8.17", features = ["impl_json_schema", "indexmap2", "url", "uuid1", "preserve_order"] }
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.128"
|
||||
sha2 = "0.10.8"
|
||||
tabled = { version = "0.15.0", optional = true }
|
||||
thiserror = "2.0.0"
|
||||
toml = "0.8.19"
|
||||
ts-rs = { version = "10.0.0", features = [
|
||||
"uuid-impl",
|
||||
"url-impl",
|
||||
"chrono-impl",
|
||||
"indexmap-impl",
|
||||
"no-serde-warnings",
|
||||
"serde-json-impl",
|
||||
] }
|
||||
ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "indexmap-impl", "no-serde-warnings", "serde-json-impl"] }
|
||||
url = { version = "2.5.3", features = ["serde"] }
|
||||
urlencoding = "2.1.3"
|
||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
||||
validator = { version = "0.19.0", features = ["derive"] }
|
||||
web-time = "1.1"
|
||||
winnow = "0.6.18"
|
||||
zip = { version = "2.0.0", default-features = false }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
js-sys = { version = "0.3.72" }
|
||||
tokio = { version = "1.41.1", features = ["sync", "time"] }
|
||||
tower-lsp = { version = "0.20.0", default-features = false, features = [
|
||||
"runtime-agnostic",
|
||||
] }
|
||||
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
|
||||
wasm-bindgen = "0.2.91"
|
||||
wasm-bindgen-futures = "0.4.44"
|
||||
web-sys = { version = "0.3.72", features = ["console"] }
|
||||
@ -89,15 +66,12 @@ web-sys = { version = "0.3.72", features = ["console"] }
|
||||
approx = "0.5"
|
||||
bson = { version = "2.13.0", features = ["uuid-1", "chrono"] }
|
||||
tokio = { version = "1.41.1", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.24.0", features = [
|
||||
"rustls-tls-native-roots",
|
||||
] }
|
||||
tokio-tungstenite = { version = "0.24.0", features = ["rustls-tls-native-roots"] }
|
||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||
|
||||
[features]
|
||||
default = ["engine"]
|
||||
cli = ["dep:clap"]
|
||||
dhat-heap = ["dep:dhat"]
|
||||
# For the lsp server, when run with stdout for rpc we want to disable println.
|
||||
# This is used for editor extensions that use the lsp server.
|
||||
disable-println = []
|
||||
|
2
src/wasm-lib/kcl/src/ast/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod modify;
|
||||
pub mod types;
|
@ -7,17 +7,18 @@ use kcmc::{
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use crate::{
|
||||
ast::types::{
|
||||
ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, PipeExpression, PipeSubstitution,
|
||||
VariableDeclarator,
|
||||
},
|
||||
engine::EngineManager,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::Point2d,
|
||||
parsing::ast::types::{
|
||||
ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, Node, PipeExpression,
|
||||
PipeSubstitution, VariableDeclarator,
|
||||
},
|
||||
source_range::{ModuleId, SourceRange},
|
||||
executor::{Point2d, SourceRange},
|
||||
Program,
|
||||
};
|
||||
|
||||
use super::types::{ModuleId, Node};
|
||||
|
||||
type Point3d = kcmc::shared::Point3d<f64>;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -184,7 +185,7 @@ pub async fn modify_ast_for_sketch(
|
||||
let recasted = program.ast.recast(&FormatOptions::default(), 0);
|
||||
|
||||
// Re-parse the ast so we get the correct source ranges.
|
||||
*program = crate::parsing::parse_str(&recasted, module_id)
|
||||
*program = crate::parser::parse_str(&recasted, module_id)
|
||||
.parse_errs_as_err()?
|
||||
.into();
|
||||
|
@ -18,8 +18,8 @@ use tower_lsp::lsp_types::{
|
||||
CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange, FoldingRangeKind, Range as LspRange, SymbolKind,
|
||||
};
|
||||
|
||||
use super::{digest::Digest, execute::execute_pipe_body};
|
||||
pub use crate::parsing::ast::types::{
|
||||
use self::execute::execute_pipe_body;
|
||||
pub use crate::ast::types::{
|
||||
condition::{ElseIf, IfExpression},
|
||||
literal_value::LiteralValue,
|
||||
none::KclNone,
|
||||
@ -27,15 +27,19 @@ pub use crate::parsing::ast::types::{
|
||||
use crate::{
|
||||
docs::StdLibFn,
|
||||
errors::KclError,
|
||||
executor::{ExecState, ExecutorContext, KclValue, Metadata, TagIdentifier},
|
||||
parsing::PIPE_OPERATOR,
|
||||
source_range::{ModuleId, SourceRange},
|
||||
executor::{ExecState, ExecutorContext, KclValue, Metadata, SourceRange, TagIdentifier},
|
||||
parser::PIPE_OPERATOR,
|
||||
std::kcl_stdlib::KclStdLibFn,
|
||||
};
|
||||
|
||||
mod condition;
|
||||
pub(crate) mod digest;
|
||||
pub(crate) mod execute;
|
||||
mod literal_value;
|
||||
mod none;
|
||||
pub(crate) mod source_range;
|
||||
|
||||
use digest::Digest;
|
||||
|
||||
pub enum Definition<'a> {
|
||||
Variable(&'a VariableDeclarator),
|
||||
@ -58,7 +62,7 @@ pub struct Node<T> {
|
||||
impl<T> Node<T> {
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
source_range: SourceRange::new(self.start, self.end, self.module_id),
|
||||
source_range: SourceRange([self.start, self.end, self.module_id.0 as usize]),
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +122,7 @@ impl<T> Node<T> {
|
||||
}
|
||||
|
||||
pub fn as_source_range(&self) -> SourceRange {
|
||||
SourceRange::new(self.start, self.end, self.module_id)
|
||||
SourceRange([self.start, self.end, self.module_id.as_usize()])
|
||||
}
|
||||
|
||||
pub fn as_source_ranges(&self) -> Vec<SourceRange> {
|
||||
@ -146,21 +150,21 @@ impl<T: fmt::Display> fmt::Display for Node<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Node<T>> for SourceRange {
|
||||
impl<T> From<Node<T>> for crate::executor::SourceRange {
|
||||
fn from(v: Node<T>) -> Self {
|
||||
Self::new(v.start, v.end, v.module_id)
|
||||
Self([v.start, v.end, v.module_id.as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<&Node<T>> for SourceRange {
|
||||
impl<T> From<&Node<T>> for crate::executor::SourceRange {
|
||||
fn from(v: &Node<T>) -> Self {
|
||||
Self::new(v.start, v.end, v.module_id)
|
||||
Self([v.start, v.end, v.module_id.as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<&BoxNode<T>> for SourceRange {
|
||||
impl<T> From<&BoxNode<T>> for crate::executor::SourceRange {
|
||||
fn from(v: &BoxNode<T>) -> Self {
|
||||
Self::new(v.start, v.end, v.module_id)
|
||||
Self([v.start, v.end, v.module_id.as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
@ -536,6 +540,7 @@ impl Program {
|
||||
/// #!/usr/bin/env python
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)]
|
||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
#[ts(export)]
|
||||
pub struct Shebang {
|
||||
@ -548,6 +553,29 @@ impl Shebang {
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier of a source file. Uses a u32 to keep the size small.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)]
|
||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
#[ts(export)]
|
||||
pub struct ModuleId(pub u32);
|
||||
|
||||
impl ModuleId {
|
||||
pub fn from_usize(id: usize) -> Self {
|
||||
Self(u32::try_from(id).expect("module ID should fit in a u32"))
|
||||
}
|
||||
|
||||
pub fn as_usize(&self) -> usize {
|
||||
usize::try_from(self.0).expect("module ID should fit in a usize")
|
||||
}
|
||||
|
||||
/// Top-level file is the one being executed.
|
||||
/// Represented by module ID of 0, i.e. the default value.
|
||||
pub fn is_top_level(&self) -> bool {
|
||||
*self == Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
#[ts(export)]
|
||||
@ -581,13 +609,13 @@ impl BodyItem {
|
||||
|
||||
impl From<BodyItem> for SourceRange {
|
||||
fn from(item: BodyItem) -> Self {
|
||||
Self::new(item.start(), item.end(), item.module_id())
|
||||
Self([item.start(), item.end(), item.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BodyItem> for SourceRange {
|
||||
fn from(item: &BodyItem) -> Self {
|
||||
Self::new(item.start(), item.end(), item.module_id())
|
||||
Self([item.start(), item.end(), item.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
@ -603,7 +631,6 @@ pub enum Expr {
|
||||
BinaryExpression(BoxNode<BinaryExpression>),
|
||||
FunctionExpression(BoxNode<FunctionExpression>),
|
||||
CallExpression(BoxNode<CallExpression>),
|
||||
CallExpressionKw(BoxNode<CallExpressionKw>),
|
||||
PipeExpression(BoxNode<PipeExpression>),
|
||||
PipeSubstitution(BoxNode<PipeSubstitution>),
|
||||
ArrayExpression(BoxNode<ArrayExpression>),
|
||||
@ -617,7 +644,7 @@ pub enum Expr {
|
||||
|
||||
impl Expr {
|
||||
pub fn get_lsp_folding_range(&self) -> Option<FoldingRange> {
|
||||
let recasted = self.recast(&FormatOptions::default(), 0, crate::unparser::ExprContext::Other);
|
||||
let recasted = self.recast(&FormatOptions::default(), 0, false);
|
||||
// If the code only has one line then we don't need to fold it.
|
||||
if recasted.lines().count() <= 1 {
|
||||
return None;
|
||||
@ -647,7 +674,6 @@ impl Expr {
|
||||
Expr::Literal(_literal) => None,
|
||||
Expr::FunctionExpression(_func_exp) => None,
|
||||
Expr::CallExpression(_call_exp) => None,
|
||||
Expr::CallExpressionKw(_call_exp) => None,
|
||||
Expr::Identifier(_ident) => None,
|
||||
Expr::TagDeclarator(_tag) => None,
|
||||
Expr::PipeExpression(pipe_exp) => Some(&pipe_exp.non_code_meta),
|
||||
@ -673,7 +699,6 @@ impl Expr {
|
||||
Expr::Literal(_) => {}
|
||||
Expr::FunctionExpression(ref mut func_exp) => func_exp.replace_value(source_range, new_value),
|
||||
Expr::CallExpression(ref mut call_exp) => call_exp.replace_value(source_range, new_value),
|
||||
Expr::CallExpressionKw(ref mut call_exp) => call_exp.replace_value(source_range, new_value),
|
||||
Expr::Identifier(_) => {}
|
||||
Expr::TagDeclarator(_) => {}
|
||||
Expr::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value),
|
||||
@ -692,7 +717,6 @@ impl Expr {
|
||||
Expr::BinaryExpression(binary_expression) => binary_expression.start,
|
||||
Expr::FunctionExpression(function_expression) => function_expression.start,
|
||||
Expr::CallExpression(call_expression) => call_expression.start,
|
||||
Expr::CallExpressionKw(call_expression) => call_expression.start,
|
||||
Expr::PipeExpression(pipe_expression) => pipe_expression.start,
|
||||
Expr::PipeSubstitution(pipe_substitution) => pipe_substitution.start,
|
||||
Expr::ArrayExpression(array_expression) => array_expression.start,
|
||||
@ -713,7 +737,6 @@ impl Expr {
|
||||
Expr::BinaryExpression(binary_expression) => binary_expression.end,
|
||||
Expr::FunctionExpression(function_expression) => function_expression.end,
|
||||
Expr::CallExpression(call_expression) => call_expression.end,
|
||||
Expr::CallExpressionKw(call_expression) => call_expression.end,
|
||||
Expr::PipeExpression(pipe_expression) => pipe_expression.end,
|
||||
Expr::PipeSubstitution(pipe_substitution) => pipe_substitution.end,
|
||||
Expr::ArrayExpression(array_expression) => array_expression.end,
|
||||
@ -735,7 +758,6 @@ impl Expr {
|
||||
function_expression.get_hover_value_for_position(pos, code)
|
||||
}
|
||||
Expr::CallExpression(call_expression) => call_expression.get_hover_value_for_position(pos, code),
|
||||
Expr::CallExpressionKw(call_expression) => call_expression.get_hover_value_for_position(pos, code),
|
||||
Expr::PipeExpression(pipe_expression) => pipe_expression.get_hover_value_for_position(pos, code),
|
||||
Expr::ArrayExpression(array_expression) => array_expression.get_hover_value_for_position(pos, code),
|
||||
Expr::ArrayRangeExpression(array_range) => array_range.get_hover_value_for_position(pos, code),
|
||||
@ -764,7 +786,6 @@ impl Expr {
|
||||
}
|
||||
Expr::FunctionExpression(_function_identifier) => {}
|
||||
Expr::CallExpression(ref mut call_expression) => call_expression.rename_identifiers(old_name, new_name),
|
||||
Expr::CallExpressionKw(ref mut call_expression) => call_expression.rename_identifiers(old_name, new_name),
|
||||
Expr::PipeExpression(ref mut pipe_expression) => pipe_expression.rename_identifiers(old_name, new_name),
|
||||
Expr::PipeSubstitution(_) => {}
|
||||
Expr::ArrayExpression(ref mut array_expression) => array_expression.rename_identifiers(old_name, new_name),
|
||||
@ -791,7 +812,6 @@ impl Expr {
|
||||
|
||||
Expr::FunctionExpression(function_identifier) => function_identifier.get_constraint_level(),
|
||||
Expr::CallExpression(call_expression) => call_expression.get_constraint_level(),
|
||||
Expr::CallExpressionKw(call_expression) => call_expression.get_constraint_level(),
|
||||
Expr::PipeExpression(pipe_expression) => pipe_expression.get_constraint_level(),
|
||||
Expr::PipeSubstitution(pipe_substitution) => ConstraintLevel::Ignore {
|
||||
source_ranges: vec![pipe_substitution.into()],
|
||||
@ -809,13 +829,13 @@ impl Expr {
|
||||
|
||||
impl From<Expr> for SourceRange {
|
||||
fn from(value: Expr) -> Self {
|
||||
Self::new(value.start(), value.end(), value.module_id())
|
||||
Self([value.start(), value.end(), value.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Expr> for SourceRange {
|
||||
fn from(value: &Expr) -> Self {
|
||||
Self::new(value.start(), value.end(), value.module_id())
|
||||
Self([value.start(), value.end(), value.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
@ -828,7 +848,6 @@ pub enum BinaryPart {
|
||||
Identifier(BoxNode<Identifier>),
|
||||
BinaryExpression(BoxNode<BinaryExpression>),
|
||||
CallExpression(BoxNode<CallExpression>),
|
||||
CallExpressionKw(BoxNode<CallExpressionKw>),
|
||||
UnaryExpression(BoxNode<UnaryExpression>),
|
||||
MemberExpression(BoxNode<MemberExpression>),
|
||||
IfExpression(BoxNode<IfExpression>),
|
||||
@ -836,13 +855,13 @@ pub enum BinaryPart {
|
||||
|
||||
impl From<BinaryPart> for SourceRange {
|
||||
fn from(value: BinaryPart) -> Self {
|
||||
Self::new(value.start(), value.end(), value.module_id())
|
||||
Self([value.start(), value.end(), value.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BinaryPart> for SourceRange {
|
||||
fn from(value: &BinaryPart) -> Self {
|
||||
Self::new(value.start(), value.end(), value.module_id())
|
||||
Self([value.start(), value.end(), value.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
@ -854,7 +873,6 @@ impl BinaryPart {
|
||||
BinaryPart::Identifier(identifier) => identifier.get_constraint_level(),
|
||||
BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_constraint_level(),
|
||||
BinaryPart::CallExpression(call_expression) => call_expression.get_constraint_level(),
|
||||
BinaryPart::CallExpressionKw(call_expression) => call_expression.get_constraint_level(),
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_constraint_level(),
|
||||
BinaryPart::IfExpression(e) => e.get_constraint_level(),
|
||||
@ -871,9 +889,6 @@ impl BinaryPart {
|
||||
BinaryPart::CallExpression(ref mut call_expression) => {
|
||||
call_expression.replace_value(source_range, new_value)
|
||||
}
|
||||
BinaryPart::CallExpressionKw(ref mut call_expression) => {
|
||||
call_expression.replace_value(source_range, new_value)
|
||||
}
|
||||
BinaryPart::UnaryExpression(ref mut unary_expression) => {
|
||||
unary_expression.replace_value(source_range, new_value)
|
||||
}
|
||||
@ -888,7 +903,6 @@ impl BinaryPart {
|
||||
BinaryPart::Identifier(identifier) => identifier.start,
|
||||
BinaryPart::BinaryExpression(binary_expression) => binary_expression.start,
|
||||
BinaryPart::CallExpression(call_expression) => call_expression.start,
|
||||
BinaryPart::CallExpressionKw(call_expression) => call_expression.start,
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.start,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.start,
|
||||
BinaryPart::IfExpression(e) => e.start,
|
||||
@ -901,7 +915,6 @@ impl BinaryPart {
|
||||
BinaryPart::Identifier(identifier) => identifier.end,
|
||||
BinaryPart::BinaryExpression(binary_expression) => binary_expression.end,
|
||||
BinaryPart::CallExpression(call_expression) => call_expression.end,
|
||||
BinaryPart::CallExpressionKw(call_expression) => call_expression.end,
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.end,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.end,
|
||||
BinaryPart::IfExpression(e) => e.end,
|
||||
@ -917,7 +930,6 @@ impl BinaryPart {
|
||||
binary_expression.get_hover_value_for_position(pos, code)
|
||||
}
|
||||
BinaryPart::CallExpression(call_expression) => call_expression.get_hover_value_for_position(pos, code),
|
||||
BinaryPart::CallExpressionKw(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::IfExpression(e) => e.get_hover_value_for_position(pos, code),
|
||||
BinaryPart::MemberExpression(member_expression) => {
|
||||
@ -937,9 +949,6 @@ impl BinaryPart {
|
||||
BinaryPart::CallExpression(ref mut call_expression) => {
|
||||
call_expression.rename_identifiers(old_name, new_name)
|
||||
}
|
||||
BinaryPart::CallExpressionKw(ref mut call_expression) => {
|
||||
call_expression.rename_identifiers(old_name, new_name)
|
||||
}
|
||||
BinaryPart::UnaryExpression(ref mut unary_expression) => {
|
||||
unary_expression.rename_identifiers(old_name, new_name)
|
||||
}
|
||||
@ -1024,6 +1033,15 @@ pub enum CommentStyle {
|
||||
Block,
|
||||
}
|
||||
|
||||
impl CommentStyle {
|
||||
fn digestable_id(&self) -> [u8; 2] {
|
||||
match &self {
|
||||
CommentStyle::Line => *b"//",
|
||||
CommentStyle::Block => *b"/*",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
#[ts(export)]
|
||||
@ -1163,7 +1181,7 @@ impl Node<ImportItem> {
|
||||
self.alias = Some(Identifier::new(new_name));
|
||||
}
|
||||
// Return implicit name.
|
||||
Some(self.identifier().to_owned())
|
||||
return Some(self.identifier().to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1246,48 +1264,19 @@ pub struct ExpressionStatement {
|
||||
pub struct CallExpression {
|
||||
pub callee: Node<Identifier>,
|
||||
pub arguments: Vec<Expr>,
|
||||
pub optional: bool,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
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 CallExpressionKw {
|
||||
pub callee: Node<Identifier>,
|
||||
pub unlabeled: Option<Expr>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub arguments: Vec<LabeledArg>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
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 LabeledArg {
|
||||
pub label: Identifier,
|
||||
pub arg: Expr,
|
||||
}
|
||||
|
||||
impl From<Node<CallExpression>> for Expr {
|
||||
fn from(call_expression: Node<CallExpression>) -> Self {
|
||||
Expr::CallExpression(Box::new(call_expression))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Node<CallExpressionKw>> for Expr {
|
||||
fn from(call_expression: Node<CallExpressionKw>) -> Self {
|
||||
Expr::CallExpressionKw(Box::new(call_expression))
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<CallExpression> {
|
||||
/// Return the constraint level for this call expression.
|
||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||
@ -1307,30 +1296,12 @@ impl Node<CallExpression> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<CallExpressionKw> {
|
||||
/// Return the constraint level for this call expression.
|
||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||
if self.arguments.is_empty() {
|
||||
return ConstraintLevel::Ignore {
|
||||
source_ranges: vec![self.into()],
|
||||
};
|
||||
}
|
||||
|
||||
// Iterate over the arguments and get the constraint level for each one.
|
||||
let mut constraint_levels = ConstraintLevels::new();
|
||||
for arg in &self.arguments {
|
||||
constraint_levels.push(arg.arg.get_constraint_level());
|
||||
}
|
||||
|
||||
constraint_levels.get_constraint_level(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl CallExpression {
|
||||
pub fn new(name: &str, arguments: Vec<Expr>) -> Result<Node<Self>, KclError> {
|
||||
Ok(Node::no_src(Self {
|
||||
callee: Identifier::new(name),
|
||||
arguments,
|
||||
optional: false,
|
||||
digest: None,
|
||||
}))
|
||||
}
|
||||
@ -1382,68 +1353,6 @@ impl CallExpression {
|
||||
}
|
||||
}
|
||||
|
||||
impl CallExpressionKw {
|
||||
pub fn new(name: &str, unlabeled: Option<Expr>, arguments: Vec<LabeledArg>) -> Result<Node<Self>, KclError> {
|
||||
Ok(Node::no_src(Self {
|
||||
callee: Identifier::new(name),
|
||||
unlabeled,
|
||||
arguments,
|
||||
digest: None,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Iterate over all arguments (labeled or not)
|
||||
pub fn iter_arguments(&self) -> impl Iterator<Item = &Expr> {
|
||||
self.unlabeled.iter().chain(self.arguments.iter().map(|arg| &arg.arg))
|
||||
}
|
||||
|
||||
/// Is at least one argument the '%' i.e. the substitution operator?
|
||||
pub fn has_substitution_arg(&self) -> bool {
|
||||
self.arguments
|
||||
.iter()
|
||||
.any(|arg| matches!(arg.arg, Expr::PipeSubstitution(_)))
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) {
|
||||
for arg in &mut self.arguments {
|
||||
arg.arg.replace_value(source_range, new_value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a hover value that includes the given character position.
|
||||
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||
let callee_source_range: SourceRange = self.callee.clone().into();
|
||||
if callee_source_range.contains(pos) {
|
||||
return Some(Hover::Function {
|
||||
name: self.callee.name.clone(),
|
||||
range: callee_source_range.to_lsp_range(code),
|
||||
});
|
||||
}
|
||||
|
||||
for (index, arg) in self.iter_arguments().enumerate() {
|
||||
let source_range: SourceRange = arg.into();
|
||||
if source_range.contains(pos) {
|
||||
return Some(Hover::Signature {
|
||||
name: self.callee.name.clone(),
|
||||
parameter_index: index as u32,
|
||||
range: source_range.to_lsp_range(code),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// 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.callee.rename(old_name, new_name);
|
||||
|
||||
for arg in &mut self.arguments {
|
||||
arg.arg.rename_identifiers(old_name, new_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A function declaration.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
@ -1488,6 +1397,13 @@ pub enum ItemVisibility {
|
||||
}
|
||||
|
||||
impl ItemVisibility {
|
||||
fn digestable_id(&self) -> [u8; 1] {
|
||||
match self {
|
||||
ItemVisibility::Default => [0],
|
||||
ItemVisibility::Export => [1],
|
||||
}
|
||||
}
|
||||
|
||||
fn is_default(&self) -> bool {
|
||||
matches!(self, Self::Default)
|
||||
}
|
||||
@ -1711,6 +1627,13 @@ pub enum VariableKind {
|
||||
}
|
||||
|
||||
impl VariableKind {
|
||||
fn digestable_id(&self) -> [u8; 1] {
|
||||
match self {
|
||||
VariableKind::Const => [2],
|
||||
VariableKind::Fn => [3],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_completion_items() -> Result<Vec<CompletionItem>> {
|
||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||
settings.inline_subschemas = true;
|
||||
@ -2278,13 +2201,13 @@ impl MemberObject {
|
||||
|
||||
impl From<MemberObject> for SourceRange {
|
||||
fn from(obj: MemberObject) -> Self {
|
||||
Self::new(obj.start(), obj.end(), obj.module_id())
|
||||
Self([obj.start(), obj.end(), obj.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&MemberObject> for SourceRange {
|
||||
fn from(obj: &MemberObject) -> Self {
|
||||
Self::new(obj.start(), obj.end(), obj.module_id())
|
||||
Self([obj.start(), obj.end(), obj.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
@ -2315,13 +2238,13 @@ impl LiteralIdentifier {
|
||||
|
||||
impl From<LiteralIdentifier> for SourceRange {
|
||||
fn from(id: LiteralIdentifier) -> Self {
|
||||
Self::new(id.start(), id.end(), id.module_id())
|
||||
Self([id.start(), id.end(), id.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&LiteralIdentifier> for SourceRange {
|
||||
fn from(id: &LiteralIdentifier) -> Self {
|
||||
Self::new(id.start(), id.end(), id.module_id())
|
||||
Self([id.start(), id.end(), id.module_id().as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
@ -2907,6 +2830,7 @@ pub enum Hover {
|
||||
|
||||
/// Format options.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FormatOptions {
|
||||
@ -3130,7 +3054,7 @@ fn ghi = (x) => {
|
||||
|
||||
ghi("things")
|
||||
"#;
|
||||
let program = crate::parsing::top_level_parse(code).unwrap();
|
||||
let program = crate::parser::top_level_parse(code).unwrap();
|
||||
let folding_ranges = program.get_lsp_folding_ranges();
|
||||
assert_eq!(folding_ranges.len(), 3);
|
||||
assert_eq!(folding_ranges[0].start_line, 29);
|
||||
@ -3145,9 +3069,9 @@ ghi("things")
|
||||
folding_ranges[1].collapsed_text,
|
||||
Some("startSketchOn('XY')".to_string())
|
||||
);
|
||||
assert_eq!(folding_ranges[2].start_line, 384);
|
||||
assert_eq!(folding_ranges[2].start_line, 390);
|
||||
assert_eq!(folding_ranges[2].end_line, 403);
|
||||
assert_eq!(folding_ranges[2].collapsed_text, Some("fn ghi(x) {".to_string()));
|
||||
assert_eq!(folding_ranges[2].collapsed_text, Some("fn ghi = (x) => {".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -3166,7 +3090,7 @@ fn ghi = (x) => {
|
||||
return x
|
||||
}
|
||||
"#;
|
||||
let program = crate::parsing::top_level_parse(code).unwrap();
|
||||
let program = crate::parser::top_level_parse(code).unwrap();
|
||||
let symbols = program.get_lsp_symbols(code).unwrap();
|
||||
assert_eq!(symbols.len(), 7);
|
||||
}
|
||||
@ -3186,7 +3110,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
}, %)
|
||||
|> extrude(h, %)
|
||||
"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
||||
|
||||
let value = program.get_non_code_meta_for_position(50);
|
||||
|
||||
@ -3209,7 +3133,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
}, %)
|
||||
|> extrude(h, %)
|
||||
"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
||||
|
||||
let value = program.get_non_code_meta_for_position(124);
|
||||
|
||||
@ -3222,7 +3146,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> xLine(5, %) // lin
|
||||
"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
||||
|
||||
let value = program.get_non_code_meta_for_position(86);
|
||||
|
||||
@ -3234,7 +3158,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
let some_program_string = r#"fn thing = (arg0: number, arg1: string, tag?: string) => {
|
||||
return arg0
|
||||
}"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
||||
|
||||
// Check the program output for the types of the parameters.
|
||||
let function = program.body.first().unwrap();
|
||||
@ -3256,7 +3180,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
let some_program_string = r#"fn thing = (arg0: number[], arg1: string[], tag?: string) => {
|
||||
return arg0
|
||||
}"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
||||
|
||||
// Check the program output for the types of the parameters.
|
||||
let function = program.body.first().unwrap();
|
||||
@ -3279,7 +3203,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
return arg0
|
||||
}"#;
|
||||
let module_id = ModuleId::default();
|
||||
let program = crate::parsing::parse_str(some_program_string, module_id).unwrap();
|
||||
let program = crate::parser::parse_str(some_program_string, module_id).unwrap();
|
||||
|
||||
// Check the program output for the types of the parameters.
|
||||
let function = program.body.first().unwrap();
|
||||
@ -3346,11 +3270,11 @@ const cylinder = startSketchOn('-XZ')
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_parse_return_type_on_functions() {
|
||||
let some_program_string = r#"fn thing(): {thing: number, things: string[], more?: string} {
|
||||
let some_program_string = r#"fn thing = () => {thing: number, things: string[], more?: string} {
|
||||
return 1
|
||||
}"#;
|
||||
let module_id = ModuleId::default();
|
||||
let program = crate::parsing::parse_str(some_program_string, module_id).unwrap();
|
||||
let program = crate::parser::parse_str(some_program_string, module_id).unwrap();
|
||||
|
||||
// Check the program output for the types of the parameters.
|
||||
let function = program.body.first().unwrap();
|
||||
@ -3372,8 +3296,8 @@ const cylinder = startSketchOn('-XZ')
|
||||
name: "thing".to_owned(),
|
||||
digest: None
|
||||
},
|
||||
13,
|
||||
18,
|
||||
23,
|
||||
module_id,
|
||||
),
|
||||
type_: Some(FnArgType::Primitive(FnArgPrimitive::Number)),
|
||||
@ -3386,8 +3310,8 @@ const cylinder = startSketchOn('-XZ')
|
||||
name: "things".to_owned(),
|
||||
digest: None
|
||||
},
|
||||
28,
|
||||
34,
|
||||
33,
|
||||
39,
|
||||
module_id,
|
||||
),
|
||||
type_: Some(FnArgType::Array(FnArgPrimitive::String)),
|
||||
@ -3400,8 +3324,8 @@ const cylinder = startSketchOn('-XZ')
|
||||
name: "more".to_owned(),
|
||||
digest: None
|
||||
},
|
||||
46,
|
||||
50,
|
||||
51,
|
||||
55,
|
||||
module_id,
|
||||
),
|
||||
type_: Some(FnArgType::Primitive(FnArgPrimitive::String)),
|
||||
@ -3538,7 +3462,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_parse_object_bool() {
|
||||
let some_program_string = r#"some_func({thing: true, other_thing: false})"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
||||
|
||||
// We want to get the bool and verify it is a bool.
|
||||
|
||||
@ -3579,4 +3503,29 @@ const cylinder = startSketchOn('-XZ')
|
||||
|
||||
assert_eq!(l.raw, "false");
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_parse_digest() {
|
||||
let prog1_string = r#"startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([5, 5], %)
|
||||
"#;
|
||||
let prog1_digest = crate::parser::top_level_parse(prog1_string).unwrap().compute_digest();
|
||||
|
||||
let prog2_string = r#"startSketchOn('XY')
|
||||
|> startProfileAt([0, 2], %)
|
||||
|> line([5, 5], %)
|
||||
"#;
|
||||
let prog2_digest = crate::parser::top_level_parse(prog2_string).unwrap().compute_digest();
|
||||
|
||||
assert!(prog1_digest != prog2_digest);
|
||||
|
||||
let prog3_string = r#"startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([5, 5], %)
|
||||
"#;
|
||||
let prog3_digest = crate::parser::top_level_parse(prog3_string).unwrap().compute_digest();
|
||||
|
||||
assert_eq!(prog1_digest, prog3_digest);
|
||||
}
|
||||
}
|
@ -1,14 +1,19 @@
|
||||
use crate::executor::SourceRange;
|
||||
|
||||
use super::BoxNode;
|
||||
use super::ConstraintLevel;
|
||||
use super::Hover;
|
||||
use super::Node;
|
||||
use super::NodeList;
|
||||
use super::{Digest, Expr};
|
||||
use databake::*;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{BoxNode, ConstraintLevel, Digest, Expr, Hover, Node, NodeList};
|
||||
use crate::SourceRange;
|
||||
|
||||
// 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::parsing::ast::types::Program;
|
||||
type IfBlock = crate::ast::types::Program;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
@ -45,7 +50,7 @@ impl Node<IfExpression> {
|
||||
impl Node<ElseIf> {
|
||||
#[allow(dead_code)]
|
||||
fn source_ranges(&self) -> Vec<SourceRange> {
|
||||
vec![SourceRange::new(self.start, self.end, self.module_id)]
|
||||
vec![SourceRange([self.start, self.end, self.module_id.as_usize()])]
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
use sha2::{Digest as DigestTrait, Sha256};
|
||||
|
||||
use super::types::{ItemVisibility, VariableKind};
|
||||
use crate::parsing::ast::types::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, CallExpressionKw,
|
||||
CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression,
|
||||
ImportItem, ImportStatement, Literal, LiteralIdentifier, MemberExpression, MemberObject, NonCodeMeta, NonCodeNode,
|
||||
NonCodeValue, ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program,
|
||||
ReturnStatement, TagDeclarator, UnaryExpression, VariableDeclaration, VariableDeclarator,
|
||||
use super::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, ElseIf, Expr,
|
||||
ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem, ImportStatement, Literal,
|
||||
LiteralIdentifier, MemberExpression, MemberObject, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression,
|
||||
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, TagDeclarator,
|
||||
UnaryExpression, VariableDeclaration, VariableDeclarator,
|
||||
};
|
||||
|
||||
/// Position-independent digest of the AST node.
|
||||
@ -94,7 +93,6 @@ impl Expr {
|
||||
Expr::BinaryExpression(be) => be.compute_digest(),
|
||||
Expr::FunctionExpression(fe) => fe.compute_digest(),
|
||||
Expr::CallExpression(ce) => ce.compute_digest(),
|
||||
Expr::CallExpressionKw(ce) => ce.compute_digest(),
|
||||
Expr::PipeExpression(pe) => pe.compute_digest(),
|
||||
Expr::PipeSubstitution(ps) => ps.compute_digest(),
|
||||
Expr::ArrayExpression(ae) => ae.compute_digest(),
|
||||
@ -119,7 +117,6 @@ impl BinaryPart {
|
||||
BinaryPart::Identifier(id) => id.compute_digest(),
|
||||
BinaryPart::BinaryExpression(be) => be.compute_digest(),
|
||||
BinaryPart::CallExpression(ce) => ce.compute_digest(),
|
||||
BinaryPart::CallExpressionKw(ce) => ce.compute_digest(),
|
||||
BinaryPart::UnaryExpression(ue) => ue.compute_digest(),
|
||||
BinaryPart::MemberExpression(me) => me.compute_digest(),
|
||||
BinaryPart::IfExpression(e) => e.compute_digest(),
|
||||
@ -210,15 +207,6 @@ impl ReturnStatement {
|
||||
});
|
||||
}
|
||||
|
||||
impl CommentStyle {
|
||||
fn digestable_id(&self) -> [u8; 2] {
|
||||
match &self {
|
||||
CommentStyle::Line => *b"//",
|
||||
CommentStyle::Block => *b"/*",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NonCodeNode {
|
||||
compute_digest!(|slf, hasher| {
|
||||
match &slf.value {
|
||||
@ -274,24 +262,6 @@ impl VariableDeclaration {
|
||||
});
|
||||
}
|
||||
|
||||
impl VariableKind {
|
||||
fn digestable_id(&self) -> [u8; 1] {
|
||||
match self {
|
||||
VariableKind::Const => [2],
|
||||
VariableKind::Fn => [3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemVisibility {
|
||||
fn digestable_id(&self) -> [u8; 1] {
|
||||
match self {
|
||||
ItemVisibility::Default => [0],
|
||||
ItemVisibility::Export => [1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VariableDeclarator {
|
||||
compute_digest!(|slf, hasher| {
|
||||
hasher.update(slf.id.compute_digest());
|
||||
@ -399,22 +369,7 @@ impl CallExpression {
|
||||
for argument in slf.arguments.iter_mut() {
|
||||
hasher.update(argument.compute_digest());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl CallExpressionKw {
|
||||
compute_digest!(|slf, hasher| {
|
||||
hasher.update(slf.callee.compute_digest());
|
||||
if let Some(ref mut unlabeled) = slf.unlabeled {
|
||||
hasher.update(unlabeled.compute_digest());
|
||||
} else {
|
||||
hasher.update("no_unlabeled");
|
||||
}
|
||||
hasher.update(slf.arguments.len().to_ne_bytes());
|
||||
for argument in slf.arguments.iter_mut() {
|
||||
hasher.update(argument.label.compute_digest());
|
||||
hasher.update(argument.arg.compute_digest());
|
||||
}
|
||||
hasher.update(if slf.optional { [1] } else { [0] });
|
||||
});
|
||||
}
|
||||
|
||||
@ -434,31 +389,3 @@ impl ElseIf {
|
||||
hasher.update(slf.then_val.compute_digest());
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_parse_digest() {
|
||||
let prog1_string = r#"startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([5, 5], %)
|
||||
"#;
|
||||
let prog1_digest = crate::parsing::top_level_parse(prog1_string).unwrap().compute_digest();
|
||||
|
||||
let prog2_string = r#"startSketchOn('XY')
|
||||
|> startProfileAt([0, 2], %)
|
||||
|> line([5, 5], %)
|
||||
"#;
|
||||
let prog2_digest = crate::parsing::top_level_parse(prog2_string).unwrap().compute_digest();
|
||||
|
||||
assert!(prog1_digest != prog2_digest);
|
||||
|
||||
let prog3_string = r#"startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([5, 5], %)
|
||||
"#;
|
||||
let prog3_digest = crate::parsing::top_level_parse(prog3_string).unwrap().compute_digest();
|
||||
|
||||
assert_eq!(prog1_digest, prog3_digest);
|
||||
}
|
||||
}
|