Compare commits
40 Commits
derive-doc
...
kcl-0.2.27
Author | SHA1 | Date | |
---|---|---|---|
8c6e62cfc4 | |||
efb067af58 | |||
2aa27eab01 | |||
9c47ac5b57 | |||
5ae1aecd74 | |||
68ae7e98f9 | |||
56771d561a | |||
f09411817c | |||
bed7ae3b8b | |||
c43510732c | |||
51f0b669a4 | |||
3cbedcd3e7 | |||
5d2fa43150 | |||
ec49b0752e | |||
3b171fb881 | |||
0548409da0 | |||
dd052b35fd | |||
46be4e7eef | |||
412d1b7a99 | |||
cfdd22af74 | |||
68a11e7aa5 | |||
3139e18dc7 | |||
d461b09a4d | |||
9c42c39ba9 | |||
aa3f40e22c | |||
4423ae16dc | |||
1d45bed649 | |||
64aac7bccc | |||
002edeaf19 | |||
5424252dac | |||
30bc85add8 | |||
39a2bd685b | |||
23a3e330f6 | |||
99dd8b87dc | |||
5ff1d9e268 | |||
ce1a37e0bc | |||
ac605c2434 | |||
f6ecdfcb02 | |||
28815eb2f1 | |||
166fa71f7e |
10
.github/workflows/build-apps.yml
vendored
@ -382,13 +382,3 @@ jobs:
|
||||
glob: '*'
|
||||
parent: false
|
||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: ${{ env.IS_RELEASE == 'true' }}
|
||||
with:
|
||||
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@v6
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
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@v6
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
|
8
.github/workflows/publish-apps-release.yml
vendored
@ -123,7 +123,7 @@ jobs:
|
||||
path: out
|
||||
glob: '*'
|
||||
parent: false
|
||||
destination: 'dl.kittycad.io/releases/modeling-app/test/new-workflow'
|
||||
destination: 'dl.kittycad.io/releases/modeling-app'
|
||||
|
||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||
run: |
|
||||
@ -132,6 +132,12 @@ 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 (eg. `v0.28.0` for `$VERSION`)
|
||||
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`.
|
||||
|
||||
```
|
||||
VERSION=$(./scripts/semantic-release.sh)
|
||||
@ -146,16 +146,14 @@ 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.
|
||||
|
||||
Once the workflow succeeds, a draft release will be created at https://github.com/KittyCAD/modeling-app/releases.
|
||||
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
|
||||
|
||||
#### 3. Manually test artifacts from the Cut Release PR
|
||||
#### 3. Manually test artifacts
|
||||
|
||||
##### 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
|
||||
@ -178,9 +176,11 @@ 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, paste in the changelog discussed in the issue, and publish the draft release created by the `build-apps` workflow from step 2.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
#### 5. Close the issue
|
||||
|
||||
@ -450,3 +450,9 @@ 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: 176 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 259 KiB |
BIN
assets/icon.ico
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 114 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, (id) => {
|
||||
|> patternTransform(n, fn(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,
|
||||
|
19920
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,20 +118,20 @@ 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,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001, %),
|
||||
-segLen(rectangleSegmentA001, %)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
}
|
||||
|
||||
rect([0, 0])
|
||||
@ -146,28 +146,31 @@ 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,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001, %),
|
||||
-segLen(rectangleSegmentA001, %)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
196.99
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
}
|
||||
|
||||
rect([0, 0])
|
||||
myRect = rect([20, 0])
|
||||
|
||||
myRect
|
||||
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
|
||||
|
@ -1,161 +0,0 @@
|
||||
---
|
||||
title: "BinaryOperator"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
Add two numbers.
|
||||
|
||||
**enum:** `+`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Subtract two numbers.
|
||||
|
||||
**enum:** `-`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Multiply two numbers.
|
||||
|
||||
**enum:** `*`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Divide two numbers.
|
||||
|
||||
**enum:** `/`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Modulo two numbers.
|
||||
|
||||
**enum:** `%`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Raise a number to a power.
|
||||
|
||||
**enum:** `^`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Are two numbers equal?
|
||||
|
||||
**enum:** `==`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Are two numbers not equal?
|
||||
|
||||
**enum:** `!=`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Is left greater than right
|
||||
|
||||
**enum:** `>`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Is left greater than or equal to right
|
||||
|
||||
**enum:** `>=`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Is left less than right
|
||||
|
||||
**enum:** `<`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Is left less than or equal to right
|
||||
|
||||
**enum:** `<=`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,161 +0,0 @@
|
||||
---
|
||||
title: "BinaryPart"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Literal`| | No |
|
||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||
| `raw` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `BinaryExpression`| | No |
|
||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
|
||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `CallExpression`| | No |
|
||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||
| `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 |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,97 +0,0 @@
|
||||
---
|
||||
title: "BodyItem"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ImportStatement`| | No |
|
||||
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
||||
| `path` |`string`| | No |
|
||||
| `raw_path` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ExpressionStatement`| | No |
|
||||
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `VariableDeclaration`| | No |
|
||||
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
||||
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
||||
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ReturnStatement`| | No |
|
||||
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
---
|
||||
title: "CommentStyle"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
Like // foo
|
||||
|
||||
**enum:** `line`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Like /* foo */
|
||||
|
||||
**enum:** `block`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
---
|
||||
title: "ElseIf"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
---
|
||||
title: "EnvironmentRef"
|
||||
excerpt: "An index pointing to an environment."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
An index pointing to an environment.
|
||||
|
||||
**Type:** `integer` (`uint`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,318 +0,0 @@
|
||||
---
|
||||
title: "Expr"
|
||||
excerpt: "An expression can be evaluated to yield a single KCL value."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
An expression can be evaluated to yield a single KCL value.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Literal`| | No |
|
||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `raw` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `BinaryExpression`| | No |
|
||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `CallExpression`| | No |
|
||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||
| `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 |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
---
|
||||
title: "FunctionExpression"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||
| `body` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
---
|
||||
title: "Identifier"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
---
|
||||
title: "ImportItem"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
||||
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
---
|
||||
title: "ItemVisibility"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**enum:** `default`, `export`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -317,7 +317,6 @@ Data for an imported geometry.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
|
||||
|
@ -1,56 +0,0 @@
|
||||
---
|
||||
title: "LiteralIdentifier"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Literal`| | No |
|
||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||
| `raw` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,47 +0,0 @@
|
||||
---
|
||||
title: "LiteralValue"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts any of the following:**
|
||||
|
||||
|
||||
**Type:** `number` (`double`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `string`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `boolean`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,57 +0,0 @@
|
||||
---
|
||||
title: "MemberObject"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `MemberExpression`| | No |
|
||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||
| `computed` |`boolean`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `name` |`string`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
---
|
||||
title: "NonCodeMeta"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `nonCodeNodes` |`object`| | No |
|
||||
| `startNodes` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
---
|
||||
title: "NonCodeNode"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
@ -1,103 +0,0 @@
|
||||
---
|
||||
title: "NonCodeValue"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `shebang`| | No |
|
||||
| `value` |`string`| | No |
|
||||
|
||||
|
||||
----
|
||||
An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `inlineComment`| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||
|
||||
|
||||
----
|
||||
A block comment. An example of this is the following: ```python,no_run /* This is a block comment */ 1 + 1 ``` Now this is important. The block comment is attached to the next line. This is always the case. Also the block comment doesn't have a new line above it. If it did it would be a `NewLineBlockComment`.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `blockComment`| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||
|
||||
|
||||
----
|
||||
A block comment that has a new line above it. The user explicitly added a new line above the block comment.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `newLineBlockComment`| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `newLine`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
---
|
||||
title: "ObjectProperty"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
---
|
||||
title: "Parameter"
|
||||
excerpt: "Parameter of a KCL function."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Parameter of a KCL function.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `identifier` |[`Identifier`](/docs/kcl/types/Identifier)| The parameter's label or name. | No |
|
||||
| `optional` |`boolean`| Is the parameter optional? | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
---
|
||||
title: "Program"
|
||||
excerpt: "A KCL program top level, or function body."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
A KCL program top level, or function body.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "Uint"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `integer` (`uint32`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
---
|
||||
title: "UnaryOperator"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
Negate a number.
|
||||
|
||||
**enum:** `-`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Negate a boolean.
|
||||
|
||||
**enum:** `!`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
---
|
||||
title: "VariableDeclarator"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
|
||||
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
---
|
||||
title: "VariableKind"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
Declare a named constant.
|
||||
|
||||
**enum:** `const`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
Declare a function.
|
||||
|
||||
**enum:** `fn`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -45,7 +45,6 @@ test.describe('integrations tests', () => {
|
||||
{
|
||||
title: 'test-sample',
|
||||
fileCount: 1,
|
||||
folderCount: 1,
|
||||
},
|
||||
],
|
||||
sortBy: 'last-modified-desc',
|
||||
@ -233,7 +232,6 @@ 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',
|
||||
|
@ -35,7 +35,7 @@ export class CmdBarFixture {
|
||||
}
|
||||
|
||||
private _serialiseCmdBar = async (): Promise<CmdBarSerialised> => {
|
||||
const reviewForm = await this.page.locator('#review-form')
|
||||
const reviewForm = this.page.locator('#review-form')
|
||||
const getHeaderArgs = async () => {
|
||||
const inputs = await this.page.getByTestId('cmd-bar-input-tab').all()
|
||||
const entries = await Promise.all(
|
||||
|
@ -4,7 +4,6 @@ import { expect } from '@playwright/test'
|
||||
interface ProjectCardState {
|
||||
title: string
|
||||
fileCount: number
|
||||
folderCount: number
|
||||
}
|
||||
|
||||
interface HomePageState {
|
||||
@ -61,15 +60,13 @@ export class HomePageFixture {
|
||||
const projectCards = await this.projectCard.all()
|
||||
const projectCardStates: Array<ProjectCardState> = []
|
||||
for (const projectCard of projectCards) {
|
||||
const [title, fileCount, folderCount] = await Promise.all([
|
||||
const [title, fileCount] = 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,6 +28,7 @@ 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
|
||||
@ -68,7 +69,7 @@ export class SceneFixture {
|
||||
x: number,
|
||||
y: number,
|
||||
{ steps }: { steps: number } = { steps: 20 }
|
||||
): [ClickHandler, MoveHandler] =>
|
||||
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
||||
[
|
||||
(clickParams?: mouseParams) => {
|
||||
if (clickParams?.pixelDiff) {
|
||||
@ -90,6 +91,16 @@ 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,6 +6,7 @@ export class ToolbarFixture {
|
||||
public page: Page
|
||||
|
||||
extrudeButton!: Locator
|
||||
offsetPlaneButton!: Locator
|
||||
startSketchBtn!: Locator
|
||||
lineBtn!: Locator
|
||||
rectangleBtn!: Locator
|
||||
@ -25,6 +26,7 @@ export class ToolbarFixture {
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
this.extrudeButton = page.getByTestId('extrude')
|
||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||
this.startSketchBtn = page.getByTestId('sketch')
|
||||
this.lineBtn = page.getByTestId('line')
|
||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||
|
@ -551,3 +551,129 @@ 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,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await app.initialise()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 700, y: 150 }
|
||||
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const expectedOutput = `plane001 = offsetPlane('XZ', 5)`
|
||||
|
||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||
})
|
||||
await test.step(`Go through the command bar flow`, async () => {
|
||||
await toolbar.offsetPlaneButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'plane',
|
||||
currentArgValue: '',
|
||||
headerArguments: { Plane: '', Distance: '' },
|
||||
highlightedHeaderArg: 'plane',
|
||||
commandName: 'Offset plane',
|
||||
})
|
||||
await clickOnXzPlane()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'distance',
|
||||
currentArgValue: '5',
|
||||
headerArguments: { Plane: '1 plane', Distance: '' },
|
||||
highlightedHeaderArg: 'distance',
|
||||
commandName: 'Offset plane',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
})
|
||||
|
||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||
await editor.expectEditor.toContain(expectedOutput)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: [expectedOutput],
|
||||
highlightedCode: '',
|
||||
})
|
||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -56,7 +56,7 @@
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-modal-promise": "^1.0.2",
|
||||
"react-router-dom": "^6.27.0",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"sketch-helpers": "^0.0.4",
|
||||
"three": "^0.166.1",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
@ -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.10.2",
|
||||
"happy-dom": "^15.11.7",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^9.1.5",
|
||||
"kill-port": "^2.0.1",
|
||||
@ -205,7 +205,7 @@
|
||||
"setimmediate": "^1.0.5",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^5.4.6",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-package-version": "^1.1.0",
|
||||
|
@ -18,13 +18,13 @@
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.16.3",
|
||||
"@codemirror/autocomplete": "6.17.0",
|
||||
"@codemirror/language": "^6.10.2",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@lezer/highlight": "^1.2.0",
|
||||
"@ts-stack/markdown": "^1.5.0",
|
||||
"json-rpc-2.0": "^1.7.0",
|
||||
"typescript": "^5.5.2",
|
||||
"typescript": "^5.7.2",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"vscode-uri": "^3.0.8"
|
||||
},
|
||||
|
@ -26,7 +26,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
|
||||
private async start(): Promise<void> {
|
||||
let contentLength: null | number = null
|
||||
let buffer = new Uint8Array()
|
||||
let buffer: Uint8Array = new Uint8Array()
|
||||
|
||||
for await (const bytes of this) {
|
||||
buffer = Bytes.append(Uint8Array, buffer, bytes)
|
||||
|
@ -95,6 +95,10 @@ export default class Queue<T>
|
||||
return this
|
||||
}
|
||||
|
||||
[Symbol.asyncDispose](): Promise<void> {
|
||||
return this.close()
|
||||
}
|
||||
|
||||
get locked(): boolean {
|
||||
return this.#stream.locked
|
||||
}
|
||||
|
@ -2,10 +2,10 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@codemirror/autocomplete@^6.16.3":
|
||||
version "6.16.3"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.16.3.tgz#04d5a4e4e44ccae1ba525d47db53a5479bf46338"
|
||||
integrity sha512-Vl/tIeRVVUCRDuOG48lttBasNQu8usGgXQawBXI7WJAiUDSFOfzflmEsZFZo48mAvAaa4FZ/4/yLLxFtdJaKYA==
|
||||
"@codemirror/autocomplete@6.17.0":
|
||||
version "6.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.17.0.tgz#24ff5fc37fd91f6439df6f4ff9c8e910cde1b053"
|
||||
integrity sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==
|
||||
dependencies:
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
@ -182,10 +182,10 @@ tslib@^2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
|
||||
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
|
||||
|
||||
typescript@^5.5.2:
|
||||
version "5.5.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507"
|
||||
integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==
|
||||
typescript@^5.7.2:
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
|
||||
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
|
@ -149,6 +149,11 @@
|
||||
"title": "Tire",
|
||||
"description": "A tire is a critical component of a vehicle that provides the necessary traction and grip between the car and the road. It supports the vehicle's weight and absorbs shocks from road irregularities."
|
||||
},
|
||||
{
|
||||
"file": "wall-e.kcl",
|
||||
"title": "WALL-E",
|
||||
"description": "An inquisitive trash compacting robot imagined by Disney Pixar Animation Studios"
|
||||
},
|
||||
{
|
||||
"file": "washer.kcl",
|
||||
"title": "Washer",
|
||||
|
@ -5,7 +5,8 @@ export COMMIT=$(git rev-parse --short HEAD)
|
||||
|
||||
# package.json
|
||||
yarn files:set-version
|
||||
echo "$(jq --arg name 'Zoo Modeling App (Nightly)' '.productName=$name' package.json --indent 2)" > package.json
|
||||
PACKAGE=$(jq '.productName="Zoo Modeling App (Nightly)" | .name="zoo-modeling-app-nightly"' package.json --indent 2)
|
||||
echo "$PACKAGE" > package.json
|
||||
|
||||
# electron-builder.yml
|
||||
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' electron-builder.yml
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
import { Coords2d, compareVec2Epsilon2 } from 'lang/std/sketch'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import * as TWEEN from '@tweenjs/tween.js'
|
||||
import { Axis } from 'lib/selections'
|
||||
import { Axis, NonCodeSelection } from 'lib/selections'
|
||||
import { type BaseUnit } from 'lib/settings/settingsTypes'
|
||||
import { CameraControls } from './CameraControls'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
@ -654,7 +654,7 @@ export class SceneInfra {
|
||||
await this.onClickCallback({ mouseEvent, intersects })
|
||||
}
|
||||
}
|
||||
updateOtherSelectionColors = (otherSelections: Axis[]) => {
|
||||
updateOtherSelectionColors = (otherSelections: NonCodeSelection[]) => {
|
||||
const axisGroup = this.scene.children.find(
|
||||
({ userData }) => userData?.type === AXIS_GROUP
|
||||
)
|
||||
|
@ -1,21 +1,26 @@
|
||||
import { useSelector } from '@xstate/react'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { Artifact } from 'lang/std/artifactGraph'
|
||||
import { CommandArgument } from 'lib/commandTypes'
|
||||
import {
|
||||
canSubmitSelectionArg,
|
||||
getSelectionType,
|
||||
getSelectionCountByType,
|
||||
getSelectionTypeDisplayText,
|
||||
} from 'lib/selections'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { toSync } from 'lib/utils'
|
||||
import { modelingMachine } from 'machines/modelingMachine'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { StateFrom } from 'xstate'
|
||||
|
||||
const semanticEntityNames: { [key: string]: Array<Artifact['type']> } = {
|
||||
const semanticEntityNames: {
|
||||
[key: string]: Array<Artifact['type'] | 'defaultPlane'>
|
||||
} = {
|
||||
face: ['wall', 'cap', 'solid2D'],
|
||||
edge: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
||||
point: [],
|
||||
plane: ['defaultPlane'],
|
||||
}
|
||||
|
||||
function getSemanticSelectionType(selectionType: Array<Artifact['type']>) {
|
||||
@ -43,21 +48,13 @@ function CommandBarSelectionInput({
|
||||
stepBack: () => void
|
||||
onSubmit: (data: unknown) => void
|
||||
}) {
|
||||
const { code } = useKclContext()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||
const [hasSubmitted, setHasSubmitted] = useState(false)
|
||||
const selection = useSelector(arg.machineActor, selectionSelector)
|
||||
const selectionsByType = useMemo(() => {
|
||||
const selectionRangeEnd = !selection
|
||||
? null
|
||||
: selection?.graphSelections[0]?.codeRef?.range[1]
|
||||
return !selectionRangeEnd || selectionRangeEnd === code.length || !selection
|
||||
? 'none'
|
||||
: !selection
|
||||
? 'none'
|
||||
: getSelectionType(selection)
|
||||
}, [selection, code])
|
||||
return getSelectionCountByType(selection)
|
||||
}, [selection])
|
||||
const canSubmitSelection = useMemo<boolean>(
|
||||
() => canSubmitSelectionArg(selectionsByType, arg),
|
||||
[selectionsByType]
|
||||
@ -67,6 +64,30 @@ function CommandBarSelectionInput({
|
||||
inputRef.current?.focus()
|
||||
}, [selection, inputRef])
|
||||
|
||||
// Show the default planes if the selection type is 'plane'
|
||||
useEffect(() => {
|
||||
if (arg.selectionTypes.includes('plane') && !canSubmitSelection) {
|
||||
toSync(() => {
|
||||
return Promise.all([
|
||||
kclManager.showPlanes(),
|
||||
kclManager.setSelectionFilter(['plane', 'object']),
|
||||
])
|
||||
}, reportRejection)()
|
||||
}
|
||||
|
||||
return () => {
|
||||
toSync(() => {
|
||||
const promises = [
|
||||
new Promise(() => kclManager.defaultSelectionFilter()),
|
||||
]
|
||||
if (!kclManager._isAstEmpty(kclManager.ast)) {
|
||||
promises.push(kclManager.hidePlanes())
|
||||
}
|
||||
return Promise.all(promises)
|
||||
}, reportRejection)()
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Fast-forward through this arg if it's marked as skippable
|
||||
// and we have a valid selection already
|
||||
useEffect(() => {
|
||||
@ -109,11 +130,15 @@ function CommandBarSelectionInput({
|
||||
{arg.warningMessage}
|
||||
</p>
|
||||
)}
|
||||
<span data-testid="cmd-bar-arg-name" className="sr-only">
|
||||
{arg.name}
|
||||
</span>
|
||||
<input
|
||||
id="selection"
|
||||
name="selection"
|
||||
ref={inputRef}
|
||||
required
|
||||
data-testid="cmd-bar-arg-value"
|
||||
placeholder="Select an entity with your mouse"
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-default"
|
||||
onKeyDown={(event) => {
|
||||
|
@ -818,15 +818,16 @@ const CustomIconMap = {
|
||||
),
|
||||
plane: (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="plane"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M4.92871 5.11391L4.43964 5.00995V4.10898V3.60898V3.10898L4.92871 3.21293L5.41778 3.31689L6.29907 3.50421V4.00421V4.50421L5.41778 4.31689V5.21786L4.92871 5.11391ZM11.8774 4.68991L8.1585 3.89945V4.39945V4.89945L11.8774 5.68991V5.18991V4.68991ZM13.7368 5.08515V5.58515V6.08515L14.6181 6.27247V7.17344L15.1071 7.2774L15.5962 7.38135V6.48038V5.98038V5.48038L15.1071 5.37643L14.6181 5.27247L13.7368 5.08515ZM15.5962 9.28233L15.1071 9.17837L14.6181 9.07441V12.8764L15.1071 12.9803L15.5962 13.0843V9.28233ZM15.5962 14.9852L15.1071 14.8813L14.6181 14.7773V15.6783L13.7368 15.491V15.991V16.491L14.6181 16.6783L15.1071 16.7823L15.5962 16.8862V16.3862V15.8862V14.9852ZM11.8774 16.0957V15.5957V15.0957L8.1585 14.3053V14.8053V15.3053L11.8774 16.0957ZM6.29907 14.91V14.41V13.91L5.41778 13.7227V12.8217L4.92871 12.7178L4.43964 12.6138V13.5148V14.0148V14.5148L4.92871 14.6188L5.41778 14.7227L6.29907 14.91ZM4.43964 10.7129L4.92871 10.8168L5.41778 10.9208V7.11883L4.92871 7.01488L4.43964 6.91092V10.7129Z"
|
||||
d="M10.9781 5.49876L14.6181 6.27247V9.99381L10.9781 9.22011V5.49876ZM10 4.29085L10.9781 4.49876L14.6181 5.27247L14.6182 5.27247L15.5963 5.48038H15.5963V6.48038V10.2017V11.2017L15.5963 11.2017V15.8862V16.8862L14.6181 16.6783L5.41784 14.7227L4.4397 14.5148V13.5148V4.10898V3.10898L5.41784 3.31689L10 4.29085ZM14.6181 10.9938V15.6783L5.41784 13.7227V4.31689L10 5.29085V9.0122V10.0122L10.9781 10.2201L14.6181 10.9938Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
@ -82,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 { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addFillet'
|
||||
import {
|
||||
ExportIntent,
|
||||
EngineConnectionStateType,
|
||||
@ -317,6 +317,7 @@ export const ModelingMachineProvider = ({
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let selections: Selections = {
|
||||
graphSelections: [],
|
||||
otherSelections: [],
|
||||
@ -375,7 +376,10 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (setSelections.selectionType === 'otherSelection') {
|
||||
if (
|
||||
setSelections.selectionType === 'axisSelection' ||
|
||||
setSelections.selectionType === 'defaultPlaneSelection'
|
||||
) {
|
||||
if (editorManager.isShiftDown) {
|
||||
selections = {
|
||||
graphSelections: selectionRanges.graphSelections,
|
||||
@ -387,20 +391,11 @@ export const ModelingMachineProvider = ({
|
||||
otherSelections: [setSelections.selection],
|
||||
}
|
||||
}
|
||||
const { engineEvents, updateSceneObjectColors } =
|
||||
handleSelectionBatch({
|
||||
selections: selections,
|
||||
})
|
||||
engineEvents &&
|
||||
engineEvents.forEach((event) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand(event)
|
||||
})
|
||||
updateSceneObjectColors()
|
||||
return {
|
||||
selectionRanges: selections,
|
||||
}
|
||||
}
|
||||
|
||||
if (setSelections.selectionType === 'completeSelection') {
|
||||
editorManager.selectRange(setSelections.selection)
|
||||
if (!sketchDetails)
|
||||
@ -581,8 +576,10 @@ export const ModelingMachineProvider = ({
|
||||
if (selectionRanges.graphSelections.length <= 0) return false
|
||||
return true
|
||||
},
|
||||
'has valid fillet selection': ({ context: { selectionRanges } }) => {
|
||||
return hasValidFilletSelection({
|
||||
'has valid edge treatment selection': ({
|
||||
context: { selectionRanges },
|
||||
}) => {
|
||||
return hasValidEdgeTreatmentSelection({
|
||||
selectionRanges,
|
||||
ast: kclManager.ast,
|
||||
code: codeManager.code,
|
||||
|
@ -13,7 +13,7 @@ import { isDesktop } from 'lib/isDesktop'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
import { SettingsFieldInput } from './SettingsFieldInput'
|
||||
import toast from 'react-hot-toast'
|
||||
import { APP_VERSION } from 'routes/Settings'
|
||||
import { APP_VERSION, PACKAGE_NAME } from 'routes/Settings'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import {
|
||||
createAndOpenNewTutorialProject,
|
||||
@ -24,6 +24,7 @@ import { ForwardedRef, forwardRef, useEffect } from 'react'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
import { toSync } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||
|
||||
interface AllSettingsFieldsProps {
|
||||
searchParamTab: SettingsLevel
|
||||
@ -245,6 +246,9 @@ export const AllSettingsFields = forwardRef(
|
||||
to inject the version from package.json */}
|
||||
App version {APP_VERSION}.{' '}
|
||||
<a
|
||||
onClick={openExternalBrowserIfDesktop(
|
||||
`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`
|
||||
)}
|
||||
href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
@ -255,6 +259,9 @@ export const AllSettingsFields = forwardRef(
|
||||
<p className="max-w-2xl mt-6">
|
||||
Don't see the feature you want? Check to see if it's on{' '}
|
||||
<a
|
||||
onClick={openExternalBrowserIfDesktop(
|
||||
'https://github.com/KittyCAD/modeling-app/discussions'
|
||||
)}
|
||||
href="https://github.com/KittyCAD/modeling-app/discussions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
@ -264,6 +271,25 @@ export const AllSettingsFields = forwardRef(
|
||||
, and start a discussion if you don't see it! Your feedback will
|
||||
help us prioritize what to build next.
|
||||
</p>
|
||||
{PACKAGE_NAME.indexOf('-nightly') === -1 && (
|
||||
<p className="max-w-2xl mt-6">
|
||||
Want to experience the latest and (hopefully) greatest from our
|
||||
main development branch?{' '}
|
||||
<a
|
||||
onClick={openExternalBrowserIfDesktop(
|
||||
'https://zoo.dev/modeling-app/download/nightly'
|
||||
)}
|
||||
href="https://zoo.dev/modeling-app/download/nightly"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Click here to grab Zoo Modeling App (Nightly)
|
||||
</a>
|
||||
. It can be installed side-by-side with the stable version
|
||||
you're running now. But careful there, a lot less testing is
|
||||
involved in their release 🤖.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,6 +17,9 @@ import {
|
||||
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',
|
||||
@ -30,6 +33,7 @@ export const Stream = () => {
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const { state, send } = useModelingContext()
|
||||
const { commandBarState } = useCommandsContext()
|
||||
const { mediaStream } = useAppStream()
|
||||
const { overallState, immediateState } = useNetworkContext()
|
||||
const [streamState, setStreamState] = useState(StreamState.Unset)
|
||||
@ -260,7 +264,15 @@ export const Stream = () => {
|
||||
if (!videoRef.current) return
|
||||
// If we're in sketch mode, don't send a engine-side select event
|
||||
if (state.matches('Sketch')) return
|
||||
if (state.matches({ idle: 'showPlanes' })) return
|
||||
// Only respect default plane selection if we're on a selection command argument
|
||||
if (
|
||||
state.matches({ idle: 'showPlanes' }) &&
|
||||
!(
|
||||
commandBarState.matches('Gathering arguments') &&
|
||||
commandBarState.context.currentArgument?.inputType === 'selection'
|
||||
)
|
||||
)
|
||||
return
|
||||
// If we're mousing up from a camera drag, don't send a select event
|
||||
if (sceneInfra.camControls.wasDragging === true) return
|
||||
|
||||
@ -270,12 +282,49 @@ 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()}
|
||||
>
|
||||
|
@ -169,6 +169,7 @@ export function useEngineConnectionSubscriptions() {
|
||||
pathToNode: artifact.codeRef.pathToNode,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Artifact is likely an extrusion face
|
||||
|
@ -23,6 +23,7 @@ import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
import { markOnce } from 'lib/performance'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
import { EntityType_type } from '@kittycad/lib/dist/types/src/models'
|
||||
|
||||
interface ExecuteArgs {
|
||||
ast?: Node<Program>
|
||||
@ -37,6 +38,7 @@ interface ExecuteArgs {
|
||||
export class KclManager {
|
||||
private _ast: Node<Program> = {
|
||||
body: [],
|
||||
shebang: null,
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
@ -204,6 +206,7 @@ export class KclManager {
|
||||
clearAst() {
|
||||
this._ast = {
|
||||
body: [],
|
||||
shebang: null,
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
@ -279,7 +282,7 @@ export class KclManager {
|
||||
this.lints = await lintAst({ ast: ast })
|
||||
|
||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||
defaultSelectionFilter(execState.memory, this.engineCommandManager)
|
||||
setSelectionFilterToDefault(execState.memory, this.engineCommandManager)
|
||||
|
||||
if (args.zoomToFit) {
|
||||
let zoomObjectId: string | undefined = ''
|
||||
@ -566,8 +569,13 @@ export class KclManager {
|
||||
}
|
||||
return Promise.all(thePromises)
|
||||
}
|
||||
/** TODO: this function is hiding unawaited asynchronous work */
|
||||
defaultSelectionFilter() {
|
||||
defaultSelectionFilter(this.programMemory, this.engineCommandManager)
|
||||
setSelectionFilterToDefault(this.programMemory, this.engineCommandManager)
|
||||
}
|
||||
/** TODO: this function is hiding unawaited asynchronous work */
|
||||
setSelectionFilter(filter: EntityType_type[]) {
|
||||
setSelectionFilter(filter, this.engineCommandManager)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -589,18 +597,35 @@ export class KclManager {
|
||||
}
|
||||
}
|
||||
|
||||
function defaultSelectionFilter(
|
||||
const defaultSelectionFilter: EntityType_type[] = [
|
||||
'face',
|
||||
'edge',
|
||||
'solid2d',
|
||||
'curve',
|
||||
'object',
|
||||
]
|
||||
|
||||
/** TODO: This function is not synchronous but is currently treated as such */
|
||||
function setSelectionFilterToDefault(
|
||||
programMemory: ProgramMemory,
|
||||
engineCommandManager: EngineCommandManager
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
programMemory.hasSketchOrSolid() &&
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'set_selection_filter',
|
||||
filter: ['face', 'edge', 'solid2d', 'curve'],
|
||||
},
|
||||
})
|
||||
setSelectionFilter(defaultSelectionFilter, engineCommandManager)
|
||||
}
|
||||
|
||||
/** TODO: This function is not synchronous but is currently treated as such */
|
||||
function setSelectionFilter(
|
||||
filter: EntityType_type[],
|
||||
engineCommandManager: EngineCommandManager
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'set_selection_filter',
|
||||
filter,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -384,7 +384,6 @@ const myVar = funcN(1, 2)`
|
||||
raw: '2',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -465,7 +464,6 @@ describe('testing pipe operator special', () => {
|
||||
],
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
type: 'CallExpression',
|
||||
@ -508,7 +506,6 @@ describe('testing pipe operator special', () => {
|
||||
end: 60,
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
type: 'CallExpression',
|
||||
@ -556,7 +553,6 @@ describe('testing pipe operator special', () => {
|
||||
value: 'myPath',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
type: 'CallExpression',
|
||||
@ -598,7 +594,6 @@ describe('testing pipe operator special', () => {
|
||||
end: 115,
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
type: 'CallExpression',
|
||||
@ -625,7 +620,6 @@ describe('testing pipe operator special', () => {
|
||||
end: 130,
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -711,7 +705,6 @@ describe('testing pipe operator special', () => {
|
||||
end: 35,
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -1765,7 +1758,6 @@ describe('test UnaryExpression', () => {
|
||||
raw: '100',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
@ -1837,11 +1829,9 @@ describe('testing nested call expressions', () => {
|
||||
raw: '3',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1879,7 +1869,6 @@ describe('should recognise callExpresions in binaryExpressions', () => {
|
||||
name: 'seg02',
|
||||
},
|
||||
],
|
||||
optional: false,
|
||||
},
|
||||
right: {
|
||||
type: 'Literal',
|
||||
|
@ -123,6 +123,7 @@ describe('Testing addSketchTo', () => {
|
||||
const result = addSketchTo(
|
||||
{
|
||||
body: [],
|
||||
shebang: null,
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
@ -527,6 +527,45 @@ export function sketchOnExtrudedFace(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an offset plane to the AST
|
||||
*/
|
||||
export function addOffsetPlane({
|
||||
node,
|
||||
defaultPlane,
|
||||
offset,
|
||||
}: {
|
||||
node: Node<Program>
|
||||
defaultPlane: DefaultPlaneStr
|
||||
offset: Expr
|
||||
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||
const modifiedAst = structuredClone(node)
|
||||
const newPlaneName = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.PLANE)
|
||||
|
||||
const newPlane = createVariableDeclaration(
|
||||
newPlaneName,
|
||||
createCallExpressionStdLib('offsetPlane', [
|
||||
createLiteral(defaultPlane.toUpperCase()),
|
||||
offset,
|
||||
])
|
||||
)
|
||||
|
||||
modifiedAst.body.push(newPlane)
|
||||
const pathToNode: PathToNode = [
|
||||
['body', ''],
|
||||
[modifiedAst.body.length - 1, 'index'],
|
||||
['declarations', 'VariableDeclaration'],
|
||||
['0', 'index'],
|
||||
['init', 'VariableDeclarator'],
|
||||
['arguments', 'CallExpression'],
|
||||
[0, 'index'],
|
||||
]
|
||||
return {
|
||||
modifiedAst,
|
||||
pathToNode,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the AST to create a new sketch using the variable declaration
|
||||
* of an offset plane. The new sketch just has to come after the offset
|
||||
@ -688,7 +727,6 @@ export function createCallExpressionStdLib(
|
||||
|
||||
name,
|
||||
},
|
||||
optional: false,
|
||||
arguments: args,
|
||||
}
|
||||
}
|
||||
@ -710,7 +748,6 @@ export function createCallExpression(
|
||||
|
||||
name,
|
||||
},
|
||||
optional: false,
|
||||
arguments: args,
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,14 @@ import {
|
||||
VariableDeclarator,
|
||||
} from '../wasm'
|
||||
import {
|
||||
EdgeTreatmentType,
|
||||
getPathToExtrudeForSegmentSelection,
|
||||
hasValidFilletSelection,
|
||||
isTagUsedInFillet,
|
||||
modifyAstWithFilletAndTag,
|
||||
hasValidEdgeTreatmentSelection,
|
||||
isTagUsedInEdgeTreatment,
|
||||
modifyAstWithEdgeTreatmentAndTag,
|
||||
FilletParameters,
|
||||
ChamferParameters,
|
||||
EdgeTreatmentParameters,
|
||||
} from './addFillet'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||
import { createLiteral } from 'lang/modifyAst'
|
||||
@ -21,7 +25,6 @@ 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'
|
||||
|
||||
@ -253,10 +256,10 @@ extrude003 = extrude(-15, sketch003)`
|
||||
})
|
||||
})
|
||||
|
||||
const runModifyAstCloneWithFilletAndTag = async (
|
||||
const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||
code: string,
|
||||
selectionSnippets: Array<string>,
|
||||
radiusValue: number,
|
||||
parameters: EdgeTreatmentParameters,
|
||||
expectedCode: string
|
||||
) => {
|
||||
// ast
|
||||
@ -274,13 +277,6 @@ const runModifyAstCloneWithFilletAndTag = async (
|
||||
]
|
||||
)
|
||||
|
||||
// radius
|
||||
const radius: KclCommandValue = {
|
||||
valueAst: createLiteral(radiusValue),
|
||||
valueText: radiusValue.toString(),
|
||||
valueCalculated: radiusValue.toString(),
|
||||
}
|
||||
|
||||
// executeAst
|
||||
await kclManager.executeAst({ ast })
|
||||
const artifactGraph = engineCommandManager.artifactGraph
|
||||
@ -299,8 +295,8 @@ const runModifyAstCloneWithFilletAndTag = async (
|
||||
otherSelections: [],
|
||||
}
|
||||
|
||||
// apply fillet to selection
|
||||
const result = modifyAstWithFilletAndTag(ast, selection, radius)
|
||||
// apply edge treatment to seleciton
|
||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||
if (err(result)) {
|
||||
return result
|
||||
}
|
||||
@ -310,9 +306,42 @@ const runModifyAstCloneWithFilletAndTag = async (
|
||||
|
||||
expect(newCode).toContain(expectedCode)
|
||||
}
|
||||
describe('Testing applyFilletToSelection', () => {
|
||||
it('should add a fillet to a specific segment', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
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 () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %)
|
||||
@ -320,9 +349,8 @@ describe('Testing applyFilletToSelection', () => {
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([0, -20], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
const segmentSnippets = ['line([0, -20], %)']
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %, $seg01)
|
||||
@ -330,17 +358,17 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet to the sketch pipe', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} to the sketch pipe`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %)
|
||||
@ -348,9 +376,8 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
|> extrude(-15, %)`
|
||||
const segmentSnippets = ['line([0, -20], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
const segmentSnippets = ['line([0, -20], %)']
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %, $seg01)
|
||||
@ -358,17 +385,17 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
|> extrude(-15, %)
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet to an already tagged segment', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} to an already tagged segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %, $seg01)
|
||||
@ -376,9 +403,8 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([0, -20], %, $seg01)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
const segmentSnippets = ['line([0, -20], %, $seg01)']
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %, $seg01)
|
||||
@ -386,17 +412,17 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet with existing tag on other segment', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} with existing tag on other segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
@ -404,9 +430,8 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
@ -414,17 +439,17 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg02] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet with existing fillet on other segment', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} with existing fillet on other segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
@ -433,9 +458,8 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 5, tags = [seg01] }, %)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
@ -444,27 +468,27 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 5, tags = [seg01] }, %)
|
||||
|> fillet({ radius = 3, tags = [seg02] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet to two segments of a single extrusion', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} with existing chamfer on other segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([20, 0], %)', 'line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
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], %)
|
||||
@ -472,17 +496,45 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg01, seg02] }, %)`
|
||||
|> chamfer({ length: 5, tags: [seg01] }, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName}: 3, tags: [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add fillets to two bodies', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} to two segments of a single extrusion`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([20, 0], %)', '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)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01, seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add ${edgeTreatmentType}s to two bodies`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -20], %)
|
||||
@ -498,13 +550,12 @@ sketch002 = startSketchOn('XY')
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude002 = extrude(-25, sketch002)` // <--- body 2
|
||||
const segmentSnippets = [
|
||||
'line([20, 0], %)',
|
||||
'line([-20, 0], %)',
|
||||
'line([0, -15], %)',
|
||||
]
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
const segmentSnippets = [
|
||||
'line([20, 0], %)',
|
||||
'line([-20, 0], %)',
|
||||
'line([0, -15], %)',
|
||||
]
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
@ -512,7 +563,7 @@ extrude002 = extrude(-25, sketch002)` // <--- body 2
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg01, seg02] }, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01, seg02] }, %)
|
||||
sketch002 = startSketchOn('XY')
|
||||
|> startProfileAt([30, 10], %)
|
||||
|> line([15, 0], %)
|
||||
@ -521,18 +572,20 @@ sketch002 = startSketchOn('XY')
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude002 = extrude(-25, sketch002)
|
||||
|> fillet({ radius = 3, tags = [seg03] }, %)` // <-- able to add a new one
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg03] }, %)` // <-- able to add a new one
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
})
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
describe('Testing isTagUsedInFillet', () => {
|
||||
describe('Testing isTagUsedInEdgeTreatment', () => {
|
||||
const code = `sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.72, 4.13], %)
|
||||
|> line([7.11, 3.48], %, $seg01)
|
||||
@ -565,7 +618,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
||||
})
|
||||
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
||||
@ -584,7 +637,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
||||
})
|
||||
it('should correctly identify no edges', () => {
|
||||
@ -603,7 +656,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual([])
|
||||
})
|
||||
})
|
||||
@ -638,7 +691,7 @@ describe('Testing button states', () => {
|
||||
}
|
||||
|
||||
// state
|
||||
const buttonState = hasValidFilletSelection({
|
||||
const buttonState = hasValidEdgeTreatmentSelection({
|
||||
ast,
|
||||
selectionRanges,
|
||||
code,
|
||||
|
@ -44,32 +44,49 @@ import {
|
||||
} from 'lib/singletons'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
// Apply Fillet To Selection
|
||||
// Edge Treatment Types
|
||||
export enum EdgeTreatmentType {
|
||||
Chamfer = 'chamfer',
|
||||
Fillet = 'fillet',
|
||||
}
|
||||
|
||||
export function applyFilletToSelection(
|
||||
export interface ChamferParameters {
|
||||
type: EdgeTreatmentType.Chamfer
|
||||
length: KclCommandValue
|
||||
}
|
||||
export interface FilletParameters {
|
||||
type: EdgeTreatmentType.Fillet
|
||||
radius: KclCommandValue
|
||||
}
|
||||
export type EdgeTreatmentParameters = ChamferParameters | FilletParameters
|
||||
|
||||
// Apply Edge Treatment (Fillet or Chamfer) To Selection
|
||||
export function applyEdgeTreatmentToSelection(
|
||||
ast: Node<Program>,
|
||||
selection: Selections,
|
||||
radius: KclCommandValue
|
||||
parameters: EdgeTreatmentParameters
|
||||
): void | Error {
|
||||
// 1. clone and modify with fillet and tag
|
||||
const result = modifyAstWithFilletAndTag(ast, selection, radius)
|
||||
// 1. clone and modify with edge treatment and tag
|
||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||
if (err(result)) return result
|
||||
const { modifiedAst, pathToFilletNode } = result
|
||||
const { modifiedAst, pathToEdgeTreatmentNode } = result
|
||||
|
||||
// 2. update ast
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
updateAstAndFocus(modifiedAst, pathToFilletNode)
|
||||
updateAstAndFocus(modifiedAst, pathToEdgeTreatmentNode)
|
||||
}
|
||||
|
||||
export function modifyAstWithFilletAndTag(
|
||||
export function modifyAstWithEdgeTreatmentAndTag(
|
||||
ast: Node<Program>,
|
||||
selections: Selections,
|
||||
radius: KclCommandValue
|
||||
): { modifiedAst: Node<Program>; pathToFilletNode: Array<PathToNode> } | Error {
|
||||
parameters: EdgeTreatmentParameters
|
||||
):
|
||||
| { modifiedAst: Node<Program>; pathToEdgeTreatmentNode: Array<PathToNode> }
|
||||
| Error {
|
||||
let clonedAst = structuredClone(ast)
|
||||
const clonedAstForGetExtrude = structuredClone(ast)
|
||||
|
||||
const astResult = insertRadiusIntoAst(clonedAst, radius)
|
||||
const astResult = insertParametersIntoAst(clonedAst, parameters)
|
||||
if (err(astResult)) return astResult
|
||||
|
||||
const artifactGraph = engineCommandManager.artifactGraph
|
||||
@ -119,21 +136,26 @@ export function modifyAstWithFilletAndTag(
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Apply fillet(s) for each extrude node (body)
|
||||
let pathToFilletNodes: Array<PathToNode> = []
|
||||
// Step 2: Apply edge treatments for each extrude node (body)
|
||||
let pathToEdgeTreatmentNodes: Array<PathToNode> = []
|
||||
for (const [pathToExtrudeNode, tagInfos] of extrudeToTagsMap.entries()) {
|
||||
// Create a fillet expression with multiple tags
|
||||
const radiusValue =
|
||||
'variableName' in radius ? radius.variableIdentifierAst : radius.valueAst
|
||||
// Create an edge treatment expression with multiple tags
|
||||
|
||||
// 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)
|
||||
|
||||
const filletCall = createCallExpressionStdLib('fillet', [
|
||||
// edge treatment call
|
||||
const edgeTreatmentCall = createCallExpressionStdLib(parameters.type, [
|
||||
createObjectExpression({
|
||||
radius: radiusValue,
|
||||
[parameterName]: parameterValue,
|
||||
tags: createArrayExpression(tagCalls),
|
||||
}),
|
||||
createPipeSubstitution(),
|
||||
@ -147,64 +169,89 @@ export function modifyAstWithFilletAndTag(
|
||||
if (err(locatedExtrudeDeclarator)) return locatedExtrudeDeclarator
|
||||
const { extrudeDeclarator } = locatedExtrudeDeclarator
|
||||
|
||||
// Modify the extrude expression to include this fillet expression
|
||||
// CallExpression - no fillet
|
||||
// PipeExpression - fillet exists or extrude in sketch pipe
|
||||
// Modify the extrude expression to include this edge treatment expression
|
||||
// CallExpression - no edge treatment
|
||||
// PipeExpression - edge treatment exists or body in sketch pipe
|
||||
|
||||
let pathToFilletNode: PathToNode = []
|
||||
let pathToEdgeTreatmentNode: PathToNode
|
||||
|
||||
if (extrudeDeclarator.init.type === 'CallExpression') {
|
||||
// 1. case when no fillet exists
|
||||
// 1. case when no edge treatment exists
|
||||
|
||||
// modify ast with new fillet call by mutating the extrude node
|
||||
// modify ast with new edge treatment call by mutating the extrude node
|
||||
extrudeDeclarator.init = createPipeExpression([
|
||||
extrudeDeclarator.init,
|
||||
filletCall,
|
||||
edgeTreatmentCall,
|
||||
])
|
||||
|
||||
// get path to the fillet node
|
||||
pathToFilletNode = getPathToNodeOfFilletLiteral(
|
||||
// get path to the edge treatment node
|
||||
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode,
|
||||
extrudeDeclarator,
|
||||
firstTag
|
||||
firstTag,
|
||||
parameters
|
||||
)
|
||||
pathToFilletNodes.push(pathToFilletNode)
|
||||
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||
} else if (extrudeDeclarator.init.type === 'PipeExpression') {
|
||||
// 2. case when fillet exists or extrude in sketch pipe
|
||||
// 2. case when edge treatment exists or extrude in sketch pipe
|
||||
|
||||
// mutate the extrude node with the new fillet call
|
||||
extrudeDeclarator.init.body.push(filletCall)
|
||||
// mutate the extrude node with the new edge treatment call
|
||||
extrudeDeclarator.init.body.push(edgeTreatmentCall)
|
||||
|
||||
// get path to the fillet node
|
||||
pathToFilletNode = getPathToNodeOfFilletLiteral(
|
||||
// get path to the edge treatment node
|
||||
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode,
|
||||
extrudeDeclarator,
|
||||
firstTag
|
||||
firstTag,
|
||||
parameters
|
||||
)
|
||||
pathToFilletNodes.push(pathToFilletNode)
|
||||
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||
} else {
|
||||
return new Error('Unsupported extrude type.')
|
||||
}
|
||||
}
|
||||
return { modifiedAst: clonedAst, pathToFilletNode: pathToFilletNodes }
|
||||
return {
|
||||
modifiedAst: clonedAst,
|
||||
pathToEdgeTreatmentNode: pathToEdgeTreatmentNodes,
|
||||
}
|
||||
}
|
||||
|
||||
function insertRadiusIntoAst(
|
||||
function insertParametersIntoAst(
|
||||
ast: Program,
|
||||
radius: KclCommandValue
|
||||
parameters: EdgeTreatmentParameters
|
||||
): { ast: Program } | Error {
|
||||
try {
|
||||
// Validate and update AST
|
||||
const newAst = structuredClone(ast)
|
||||
|
||||
// handle radius parameter
|
||||
if (
|
||||
'variableName' in radius &&
|
||||
radius.variableName &&
|
||||
radius.insertIndex !== undefined
|
||||
parameters.type === EdgeTreatmentType.Fillet &&
|
||||
'variableName' in parameters.radius &&
|
||||
parameters.radius.variableName &&
|
||||
parameters.radius.insertIndex !== undefined
|
||||
) {
|
||||
const newAst = structuredClone(ast)
|
||||
newAst.body.splice(radius.insertIndex, 0, radius.variableDeclarationAst)
|
||||
return { ast: newAst }
|
||||
newAst.body.splice(
|
||||
parameters.radius.insertIndex,
|
||||
0,
|
||||
parameters.radius.variableDeclarationAst
|
||||
)
|
||||
}
|
||||
return { ast }
|
||||
// handle length parameter
|
||||
if (
|
||||
parameters.type === EdgeTreatmentType.Chamfer &&
|
||||
'variableName' in parameters.length &&
|
||||
parameters.length.variableName &&
|
||||
parameters.length.insertIndex !== undefined
|
||||
) {
|
||||
newAst.body.splice(
|
||||
parameters.length.insertIndex,
|
||||
0,
|
||||
parameters.length.variableDeclarationAst
|
||||
)
|
||||
}
|
||||
|
||||
// handle upcoming parameters here (for blend, bevel, etc.)
|
||||
return { ast: newAst }
|
||||
} catch (error) {
|
||||
return new Error(`Failed to handle AST: ${(error as Error).message}`)
|
||||
}
|
||||
@ -248,10 +295,10 @@ export function getPathToExtrudeForSegmentSelection(
|
||||
|
||||
async function updateAstAndFocus(
|
||||
modifiedAst: Node<Program>,
|
||||
pathToFilletNode: Array<PathToNode>
|
||||
pathToEdgeTreatmentNode: Array<PathToNode>
|
||||
) {
|
||||
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||
focusPath: pathToFilletNode,
|
||||
focusPath: pathToEdgeTreatmentNode,
|
||||
})
|
||||
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
|
||||
@ -340,27 +387,38 @@ function locateExtrudeDeclarator(
|
||||
return { extrudeDeclarator }
|
||||
}
|
||||
|
||||
function getPathToNodeOfFilletLiteral(
|
||||
function getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode: PathToNode,
|
||||
extrudeDeclarator: VariableDeclarator,
|
||||
tag: Identifier | CallExpression
|
||||
tag: Identifier | CallExpression,
|
||||
parameters: EdgeTreatmentParameters
|
||||
): PathToNode {
|
||||
let pathToFilletObj: PathToNode = []
|
||||
let inFillet = false
|
||||
let pathToEdgeTreatmentObj: PathToNode = []
|
||||
let inEdgeTreatment = false
|
||||
|
||||
traverse(extrudeDeclarator.init, {
|
||||
enter(node, path) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.name === parameters.type
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
}
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
if (!hasTag(node, tag)) return false
|
||||
pathToFilletObj = getPathToRadiusLiteral(node, path)
|
||||
pathToEdgeTreatmentObj = getPathToEdgeTreatmentParameterLiteral(
|
||||
node,
|
||||
path,
|
||||
parameters
|
||||
)
|
||||
}
|
||||
},
|
||||
leave(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.name === parameters.type
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
}
|
||||
},
|
||||
})
|
||||
@ -375,7 +433,7 @@ function getPathToNodeOfFilletLiteral(
|
||||
|
||||
return [
|
||||
...pathToExtrudeNode.slice(0, indexOfPipeExpression),
|
||||
...pathToFilletObj,
|
||||
...pathToEdgeTreatmentObj,
|
||||
]
|
||||
}
|
||||
|
||||
@ -408,23 +466,62 @@ function hasTag(
|
||||
})
|
||||
}
|
||||
|
||||
function getPathToRadiusLiteral(node: ObjectExpression, path: any): PathToNode {
|
||||
let pathToFilletObj = path
|
||||
function getPathToEdgeTreatmentParameterLiteral(
|
||||
node: ObjectExpression,
|
||||
path: any,
|
||||
parameters: EdgeTreatmentParameters
|
||||
): PathToNode {
|
||||
let pathToEdgeTreatmentObj = path
|
||||
const parameterResult = getParameterNameAndValue(parameters)
|
||||
if (err(parameterResult)) return pathToEdgeTreatmentObj
|
||||
const { parameterName } = parameterResult
|
||||
|
||||
node.properties.forEach((prop, index) => {
|
||||
if (prop.key.name === 'radius') {
|
||||
pathToFilletObj.push(
|
||||
if (prop.key.name === parameterName) {
|
||||
pathToEdgeTreatmentObj.push(
|
||||
['properties', 'ObjectExpression'],
|
||||
[index, 'index'],
|
||||
['value', 'Property']
|
||||
)
|
||||
}
|
||||
})
|
||||
return pathToFilletObj
|
||||
return pathToEdgeTreatmentObj
|
||||
}
|
||||
|
||||
function getParameterNameAndValue(
|
||||
parameters: EdgeTreatmentParameters
|
||||
): { parameterName: string; parameterValue: Expr } | Error {
|
||||
if (parameters.type === EdgeTreatmentType.Fillet) {
|
||||
const parameterValue =
|
||||
'variableName' in parameters.radius
|
||||
? parameters.radius.variableIdentifierAst
|
||||
: parameters.radius.valueAst
|
||||
return { parameterName: 'radius', parameterValue }
|
||||
} else if (parameters.type === EdgeTreatmentType.Chamfer) {
|
||||
const parameterValue =
|
||||
'variableName' in parameters.length
|
||||
? parameters.length.variableIdentifierAst
|
||||
: parameters.length.valueAst
|
||||
return { parameterName: 'length', parameterValue }
|
||||
} else {
|
||||
return new Error('Unsupported edge treatment type}')
|
||||
}
|
||||
}
|
||||
|
||||
// Type Guards
|
||||
function isEdgeTreatmentType(name: string): name is EdgeTreatmentType {
|
||||
return name === EdgeTreatmentType.Chamfer || name === EdgeTreatmentType.Fillet
|
||||
}
|
||||
function isEdgeType(name: string): name is EdgeTypes {
|
||||
return (
|
||||
name === 'getNextAdjacentEdge' ||
|
||||
name === 'getPreviousAdjacentEdge' ||
|
||||
name === 'getOppositeEdge'
|
||||
)
|
||||
}
|
||||
|
||||
// Button states
|
||||
|
||||
export const hasValidFilletSelection = ({
|
||||
export const hasValidEdgeTreatmentSelection = ({
|
||||
selectionRanges,
|
||||
ast,
|
||||
code,
|
||||
@ -433,11 +530,14 @@ export const hasValidFilletSelection = ({
|
||||
ast: Node<Program>
|
||||
code: string
|
||||
}) => {
|
||||
// check if there is anything filletable in the scene
|
||||
// check if there is anything valid for the edge treatment in the scene
|
||||
let extrudeExists = false
|
||||
traverse(ast, {
|
||||
enter(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'extrude') {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
(node.callee.name === 'extrude' || node.callee.name === 'revolve')
|
||||
) {
|
||||
extrudeExists = true
|
||||
}
|
||||
},
|
||||
@ -494,32 +594,39 @@ export const hasValidFilletSelection = ({
|
||||
},
|
||||
})
|
||||
|
||||
// check if tag is used in fillet
|
||||
// check if tag is used in edge treatment
|
||||
if (tagExists && selection.artifact) {
|
||||
// create tag call
|
||||
let tagCall: Expr = getEdgeTagCall(tag, selection.artifact)
|
||||
|
||||
// check if tag is used in fillet
|
||||
let inFillet = false
|
||||
let tagUsedInFillet = false
|
||||
// check if tag is used in edge treatment
|
||||
let inEdgeTreatment = false
|
||||
let tagUsedInEdgeTreatment = false
|
||||
|
||||
traverse(ast, {
|
||||
enter(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
}
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
if (hasTag(node, tagCall)) {
|
||||
tagUsedInFillet = true
|
||||
tagUsedInEdgeTreatment = true
|
||||
}
|
||||
}
|
||||
},
|
||||
leave(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
}
|
||||
},
|
||||
})
|
||||
if (tagUsedInFillet) {
|
||||
if (tagUsedInEdgeTreatment) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -533,7 +640,7 @@ type EdgeTypes =
|
||||
| 'getPreviousAdjacentEdge'
|
||||
| 'getOppositeEdge'
|
||||
|
||||
export const isTagUsedInFillet = ({
|
||||
export const isTagUsedInEdgeTreatment = ({
|
||||
ast,
|
||||
callExp,
|
||||
}: {
|
||||
@ -543,16 +650,21 @@ export const isTagUsedInFillet = ({
|
||||
const tag = getTagFromCallExpression(callExp)
|
||||
if (err(tag)) return []
|
||||
|
||||
let inFillet = false
|
||||
let inEdgeTreatment = false
|
||||
let inObj = false
|
||||
let inTagHelper: EdgeTypes | '' = ''
|
||||
const edges: Array<EdgeTypes> = []
|
||||
|
||||
traverse(ast, {
|
||||
enter: (node) => {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
// Check if we are entering an edge treatment call
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
}
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
node.properties.forEach((prop) => {
|
||||
if (
|
||||
prop.key.name === 'tags' &&
|
||||
@ -564,17 +676,15 @@ export const isTagUsedInFillet = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inFillet &&
|
||||
inEdgeTreatment &&
|
||||
node.type === 'CallExpression' &&
|
||||
(node.callee.name === 'getOppositeEdge' ||
|
||||
node.callee.name === 'getNextAdjacentEdge' ||
|
||||
node.callee.name === 'getPreviousAdjacentEdge')
|
||||
isEdgeType(node.callee.name)
|
||||
) {
|
||||
inTagHelper = node.callee.name
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inFillet &&
|
||||
inEdgeTreatment &&
|
||||
!inTagHelper &&
|
||||
node.type === 'Identifier' &&
|
||||
node.name === tag
|
||||
@ -583,7 +693,7 @@ export const isTagUsedInFillet = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inFillet &&
|
||||
inEdgeTreatment &&
|
||||
inTagHelper &&
|
||||
node.type === 'Identifier' &&
|
||||
node.name === tag
|
||||
@ -592,10 +702,13 @@ export const isTagUsedInFillet = ({
|
||||
}
|
||||
},
|
||||
leave: (node) => {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
}
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
node.properties.forEach((prop) => {
|
||||
if (
|
||||
prop.key.name === 'tags' &&
|
||||
@ -607,11 +720,9 @@ export const isTagUsedInFillet = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inFillet &&
|
||||
inEdgeTreatment &&
|
||||
node.type === 'CallExpression' &&
|
||||
(node.callee.name === 'getOppositeEdge' ||
|
||||
node.callee.name === 'getNextAdjacentEdge' ||
|
||||
node.callee.name === 'getPreviousAdjacentEdge')
|
||||
isEdgeType(node.callee.name)
|
||||
) {
|
||||
inTagHelper = ''
|
||||
}
|
||||
|
@ -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' } } }
|
||||
|
||||
|
@ -1823,6 +1823,7 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||
modifiedAst: {
|
||||
start: 0,
|
||||
end: 0,
|
||||
shebang: null,
|
||||
moduleId: 0,
|
||||
body: [],
|
||||
|
||||
|
@ -1,382 +0,0 @@
|
||||
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,7 +3,6 @@ import init, {
|
||||
recast_wasm,
|
||||
execute_wasm,
|
||||
kcl_lint,
|
||||
lexer_wasm,
|
||||
modify_ast_for_sketch_wasm,
|
||||
is_points_ccw,
|
||||
get_tangential_arc_to_info,
|
||||
@ -24,7 +23,6 @@ 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'
|
||||
@ -507,10 +505,6 @@ export const modifyGrid = async (
|
||||
}
|
||||
}
|
||||
|
||||
export function lexer(str: string): Token[] | Error {
|
||||
return lexer_wasm(str)
|
||||
}
|
||||
|
||||
export const modifyAstForSketch = async (
|
||||
engineCommandManager: EngineCommandManager,
|
||||
ast: Node<Program>,
|
||||
|
@ -40,6 +40,10 @@ export type ModelingCommandSchema = {
|
||||
selection: Selections
|
||||
radius: KclCommandValue
|
||||
}
|
||||
'Offset plane': {
|
||||
plane: Selections
|
||||
distance: KclCommandValue
|
||||
}
|
||||
'change tool': {
|
||||
tool: SketchTool
|
||||
}
|
||||
@ -276,6 +280,24 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
},
|
||||
},
|
||||
},
|
||||
'Offset plane': {
|
||||
description: 'Offset a plane.',
|
||||
icon: 'plane',
|
||||
args: {
|
||||
plane: {
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['plane'],
|
||||
multiple: false,
|
||||
required: true,
|
||||
skip: true,
|
||||
},
|
||||
distance: {
|
||||
inputType: 'kcl',
|
||||
defaultValue: KCL_DEFAULT_LENGTH,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Fillet: {
|
||||
description: 'Fillet edge',
|
||||
icon: 'fillet',
|
||||
|
@ -54,6 +54,7 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||
EXTRUDE: 'extrude',
|
||||
SEGMENT: 'seg',
|
||||
REVOLVE: 'revolve',
|
||||
PLANE: 'plane',
|
||||
} as const
|
||||
/** The default KCL length expression */
|
||||
export const KCL_DEFAULT_LENGTH = `5`
|
||||
|
@ -46,9 +46,21 @@ describe('desktop utilities', () => {
|
||||
'project-without-kcl-files',
|
||||
'another-valid-project',
|
||||
],
|
||||
'/test/projects/valid-project': ['file1.kcl', 'file2.stp'],
|
||||
'/test/projects/valid-project': [
|
||||
'file1.kcl',
|
||||
'file2.stp',
|
||||
'file3.kcl',
|
||||
'directory1',
|
||||
],
|
||||
'/test/projects/valid-project/directory1': [],
|
||||
'/test/projects/project-without-kcl-files': ['file3.glb'],
|
||||
'/test/projects/another-valid-project': ['file4.kcl'],
|
||||
'/test/projects/another-valid-project': [
|
||||
'file4.kcl',
|
||||
'directory2',
|
||||
'directory3',
|
||||
],
|
||||
'/test/projects/another-valid-project/directory2': [],
|
||||
'/test/projects/another-valid-project/directory3': [],
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
@ -119,6 +131,15 @@ 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,7 +307,10 @@ const directoryCount = (file: FileEntry) => {
|
||||
let count = 0
|
||||
if (file.children) {
|
||||
for (let entry of file.children) {
|
||||
count += 1
|
||||
// We only want to count FileEntries with children, e.g. folders
|
||||
if (entry.children !== null) {
|
||||
count += 1
|
||||
}
|
||||
directoryCount(entry)
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
} from 'lang/queryAst'
|
||||
import { CommandArgument } from './commandTypes'
|
||||
import {
|
||||
DefaultPlaneStr,
|
||||
getParentGroup,
|
||||
SEGMENT_BODIES_PLUS_PROFILE_START,
|
||||
} from 'clientSideScene/sceneEntities'
|
||||
@ -46,6 +47,10 @@ export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b'
|
||||
export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
|
||||
|
||||
export type Axis = 'y-axis' | 'x-axis' | 'z-axis'
|
||||
export type DefaultPlaneSelection = {
|
||||
name: DefaultPlaneStr
|
||||
id: string
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link Artifact} instead. */
|
||||
type Selection__old =
|
||||
@ -72,9 +77,11 @@ type Selection__old =
|
||||
// TODO this is a temporary measure that well be made redundant with: https://github.com/KittyCAD/modeling-app/pull/3836
|
||||
secondaryRange: SourceRange
|
||||
}
|
||||
export type NonCodeSelection = Axis | DefaultPlaneSelection
|
||||
|
||||
/** @deprecated Use {@link Selection} instead. */
|
||||
export type Selections__old = {
|
||||
otherSelections: Axis[]
|
||||
otherSelections: NonCodeSelection[]
|
||||
codeBasedSelections: Selection__old[]
|
||||
}
|
||||
export interface Selection {
|
||||
@ -82,7 +89,7 @@ export interface Selection {
|
||||
codeRef: CodeRef
|
||||
}
|
||||
export type Selections = {
|
||||
otherSelections: Array<Axis>
|
||||
otherSelections: Array<NonCodeSelection>
|
||||
graphSelections: Array<Selection>
|
||||
}
|
||||
|
||||
@ -172,11 +179,31 @@ export async function getEventForSelectWithPoint({
|
||||
return {
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
selectionType: 'otherSelection',
|
||||
selectionType: 'axisSelection',
|
||||
selection: X_AXIS_UUID === data.entity_id ? 'x-axis' : 'y-axis',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Check for default plane selection
|
||||
const foundDefaultPlane =
|
||||
engineCommandManager.defaultPlanes !== null &&
|
||||
Object.entries(engineCommandManager.defaultPlanes).find(
|
||||
([, plane]) => plane === data.entity_id
|
||||
)
|
||||
if (foundDefaultPlane) {
|
||||
return {
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
selectionType: 'defaultPlaneSelection',
|
||||
selection: {
|
||||
name: foundDefaultPlane[0] as DefaultPlaneStr,
|
||||
id: data.entity_id,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let _artifact = engineCommandManager.artifactGraph.get(data.entity_id)
|
||||
const codeRefs = getCodeRefsByArtifactId(
|
||||
data.entity_id,
|
||||
@ -207,7 +234,7 @@ export function getEventForSegmentSelection(
|
||||
return {
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
selectionType: 'otherSelection',
|
||||
selectionType: 'axisSelection',
|
||||
selection: obj?.userData?.type === X_AXIS ? 'x-axis' : 'y-axis',
|
||||
},
|
||||
}
|
||||
@ -272,7 +299,6 @@ export function handleSelectionBatch({
|
||||
}): {
|
||||
engineEvents: Models['WebSocketRequest_type'][]
|
||||
codeMirrorSelection: EditorSelection
|
||||
otherSelections: Axis[]
|
||||
updateSceneObjectColors: () => void
|
||||
} {
|
||||
const ranges: ReturnType<typeof EditorSelection.cursor>[] = []
|
||||
@ -303,7 +329,6 @@ export function handleSelectionBatch({
|
||||
ranges,
|
||||
selections.graphSelections.length - 1
|
||||
),
|
||||
otherSelections: selections.otherSelections,
|
||||
updateSceneObjectColors: () =>
|
||||
updateSceneObjectColors(selections.graphSelections),
|
||||
}
|
||||
@ -314,7 +339,6 @@ export function handleSelectionBatch({
|
||||
0
|
||||
),
|
||||
engineEvents,
|
||||
otherSelections: selections.otherSelections,
|
||||
updateSceneObjectColors: () =>
|
||||
updateSceneObjectColors(selections.graphSelections),
|
||||
}
|
||||
@ -536,7 +560,8 @@ export function canSweepSelection(selection: Selections) {
|
||||
}
|
||||
|
||||
// This accounts for non-geometry selections under "other"
|
||||
export type ResolvedSelectionType = [Artifact['type'] | 'other', number]
|
||||
export type ResolvedSelectionType = Artifact['type'] | 'other'
|
||||
export type SelectionCountsByType = Map<ResolvedSelectionType, number>
|
||||
|
||||
/**
|
||||
* In the future, I'd like this function to properly return the type of each selected entity based on
|
||||
@ -545,28 +570,48 @@ export type ResolvedSelectionType = [Artifact['type'] | 'other', number]
|
||||
* @param selection
|
||||
* @returns
|
||||
*/
|
||||
export function getSelectionType(
|
||||
export function getSelectionCountByType(
|
||||
selection?: Selections
|
||||
): ResolvedSelectionType[] {
|
||||
if (!selection) return []
|
||||
const selectionsWithArtifacts = selection.graphSelections.filter(
|
||||
(s) => !!s.artifact
|
||||
): SelectionCountsByType | 'none' {
|
||||
const selectionsByType: SelectionCountsByType = new Map()
|
||||
if (
|
||||
!selection ||
|
||||
(!selection.graphSelections.length && !selection.otherSelections.length)
|
||||
)
|
||||
const firstSelection = selectionsWithArtifacts[0]
|
||||
const firstSelectionType = firstSelection?.artifact?.type
|
||||
if (!firstSelectionType) return []
|
||||
const selectionsWithSameType = selectionsWithArtifacts.filter(
|
||||
(s) => s.artifact?.type === firstSelection.artifact?.type
|
||||
)
|
||||
return [[firstSelectionType, selectionsWithSameType.length]]
|
||||
return 'none'
|
||||
|
||||
function incrementOrInitializeSelectionType(type: ResolvedSelectionType) {
|
||||
const count = selectionsByType.get(type) || 0
|
||||
selectionsByType.set(type, count + 1)
|
||||
}
|
||||
|
||||
selection.otherSelections.forEach((selection) => {
|
||||
if (typeof selection === 'string') {
|
||||
incrementOrInitializeSelectionType('other')
|
||||
} else if ('name' in selection) {
|
||||
incrementOrInitializeSelectionType('plane')
|
||||
}
|
||||
})
|
||||
|
||||
selection.graphSelections.forEach((selection) => {
|
||||
if (!selection.artifact) {
|
||||
incrementOrInitializeSelectionType('other')
|
||||
return
|
||||
}
|
||||
incrementOrInitializeSelectionType(selection.artifact.type)
|
||||
})
|
||||
|
||||
return selectionsByType
|
||||
}
|
||||
|
||||
export function getSelectionTypeDisplayText(
|
||||
selection?: Selections
|
||||
): string | null {
|
||||
const selectionsByType = getSelectionType(selection)
|
||||
const selectionsByType = getSelectionCountByType(selection)
|
||||
if (selectionsByType === 'none') return null
|
||||
|
||||
return (selectionsByType as Exclude<typeof selectionsByType, 'none'>)
|
||||
return selectionsByType
|
||||
.entries()
|
||||
.map(
|
||||
// Hack for showing "face" instead of "extrude-wall" in command bar text
|
||||
([type, count]) =>
|
||||
@ -575,16 +620,17 @@ export function getSelectionTypeDisplayText(
|
||||
.replace('solid2D', 'face')
|
||||
.replace('segment', 'face')}${count > 1 ? 's' : ''}`
|
||||
)
|
||||
.toArray()
|
||||
.join(', ')
|
||||
}
|
||||
|
||||
export function canSubmitSelectionArg(
|
||||
selectionsByType: 'none' | ResolvedSelectionType[],
|
||||
selectionsByType: 'none' | Map<ResolvedSelectionType, number>,
|
||||
argument: CommandArgument<unknown> & { inputType: 'selection' }
|
||||
) {
|
||||
return (
|
||||
selectionsByType !== 'none' &&
|
||||
selectionsByType.every(([type, count]) => {
|
||||
selectionsByType.entries().every(([type, count]) => {
|
||||
const foundIndex = argument.selectionTypes.findIndex((s) => s === type)
|
||||
return (
|
||||
foundIndex !== -1 &&
|
||||
|
@ -252,10 +252,15 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
||||
[
|
||||
{
|
||||
id: 'plane-offset',
|
||||
onClick: () =>
|
||||
console.error('Plane through normal not yet implemented'),
|
||||
onClick: ({ commandBarSend }) => {
|
||||
commandBarSend({
|
||||
type: 'Find and select command',
|
||||
data: { name: 'Offset plane', groupId: 'modeling' },
|
||||
})
|
||||
},
|
||||
hotkey: 'O',
|
||||
icon: 'plane',
|
||||
status: 'unavailable',
|
||||
status: 'available',
|
||||
title: 'Offset plane',
|
||||
description: 'Create a plane parallel to an existing plane.',
|
||||
links: [],
|
||||
|
@ -34,6 +34,8 @@ export function useCalculateKclExpression({
|
||||
} {
|
||||
const { programMemory, code } = useKclContext()
|
||||
const { context } = useModelingContext()
|
||||
// If there is no selection, use the end of the code
|
||||
// so all variables are available
|
||||
const selectionRange:
|
||||
| (typeof context)['selectionRanges']['graphSelections'][number]['codeRef']['range']
|
||||
| undefined = context.selectionRanges.graphSelections[0]?.codeRef?.range
|
||||
@ -72,11 +74,12 @@ export function useCalculateKclExpression({
|
||||
}, [programMemory, newVariableName])
|
||||
|
||||
useEffect(() => {
|
||||
if (!programMemory || !selectionRange) return
|
||||
if (!programMemory) return
|
||||
const varInfo = findAllPreviousVariables(
|
||||
kclManager.ast,
|
||||
kclManager.programMemory,
|
||||
selectionRange
|
||||
// If there is no selection, use the end of the code
|
||||
selectionRange || [code.length, code.length]
|
||||
)
|
||||
setAvailableVarInfo(varInfo)
|
||||
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||
|
@ -26,6 +26,10 @@ export const APP_VERSION =
|
||||
window.electron.packageJson.version
|
||||
: 'main'
|
||||
|
||||
export const PACKAGE_NAME = isDesktop()
|
||||
? window.electron.packageJson.name
|
||||
: 'zoo-modeling-app'
|
||||
|
||||
export const Settings = () => {
|
||||
const navigate = useNavigate()
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
|
173
src/wasm-lib/Cargo.lock
generated
@ -228,6 +228,15 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace-ext"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
@ -434,9 +443,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.20"
|
||||
version = "4.5.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -444,9 +453,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.20"
|
||||
version = "4.5.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -765,6 +774,22 @@ 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"
|
||||
@ -1625,6 +1650,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_ci"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
@ -1675,7 +1706,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.25"
|
||||
version = "0.2.27"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1690,6 +1721,7 @@ dependencies = [
|
||||
"dashmap 6.1.0",
|
||||
"databake",
|
||||
"derive-docs",
|
||||
"dhat",
|
||||
"expectorate",
|
||||
"fnv",
|
||||
"form_urlencoded",
|
||||
@ -1708,6 +1740,7 @@ dependencies = [
|
||||
"kittycad-modeling-cmds",
|
||||
"lazy_static",
|
||||
"measurements",
|
||||
"miette",
|
||||
"mime_guess",
|
||||
"parse-display 0.9.1",
|
||||
"pretty_assertions",
|
||||
@ -1733,6 +1766,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"web-time",
|
||||
"winnow",
|
||||
"zip",
|
||||
]
|
||||
@ -1806,9 +1840,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds"
|
||||
version = "0.2.76"
|
||||
version = "0.2.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2394fe2b28a1c6bd524dec1dbcd7e839c2782a6ecc743085e122cde77ee19cfa"
|
||||
checksum = "3b77259b37acafa360d98af27431ac394bc8899eeed7037513832ddbee856811"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -1971,6 +2005,37 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "7.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"backtrace-ext",
|
||||
"cfg-if",
|
||||
"miette-derive",
|
||||
"owo-colors",
|
||||
"supports-color",
|
||||
"supports-hyperlinks",
|
||||
"supports-unicode",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
"thiserror 1.0.68",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "7.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
@ -2009,6 +2074,12 @@ 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"
|
||||
@ -2163,6 +2234,12 @@ dependencies = [
|
||||
"thiserror 1.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56"
|
||||
|
||||
[[package]]
|
||||
name = "papergrid"
|
||||
version = "0.11.0"
|
||||
@ -2591,7 +2668,7 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 1.0.68",
|
||||
@ -2608,7 +2685,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"rand 0.8.5",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls",
|
||||
"slab",
|
||||
"thiserror 1.0.68",
|
||||
@ -2948,6 +3025,12 @@ 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"
|
||||
@ -2982,9 +3065,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.13"
|
||||
version = "0.23.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
|
||||
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
@ -3018,9 +3101,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
@ -3311,6 +3394,12 @@ version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.7"
|
||||
@ -3396,6 +3485,27 @@ version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "supports-color"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f"
|
||||
dependencies = [
|
||||
"is_ci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "supports-hyperlinks"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee"
|
||||
|
||||
[[package]]
|
||||
name = "supports-unicode"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@ -3496,6 +3606,27 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
||||
dependencies = [
|
||||
"smawk",
|
||||
"unicode-linebreak",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.68"
|
||||
@ -3536,6 +3667,12 @@ 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"
|
||||
@ -3614,9 +3751,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.40.0"
|
||||
version = "1.41.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@ -3955,6 +4092,12 @@ version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
|
@ -16,7 +16,7 @@ gloo-utils = "0.2.0"
|
||||
kcl-lib = { path = "kcl" }
|
||||
kittycad.workspace = true
|
||||
serde_json = "1.0.128"
|
||||
tokio = { version = "1.40.0", features = ["sync"] }
|
||||
tokio = { version = "1.41.1", features = ["sync"] }
|
||||
toml = "0.8.19"
|
||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.91"
|
||||
@ -29,7 +29,7 @@ kittycad = { workspace = true, default-features = true }
|
||||
kittycad-modeling-cmds = { workspace = true }
|
||||
pretty_assertions = "1.4.1"
|
||||
reqwest = { version = "0.12", default-features = false }
|
||||
tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
tokio = { version = "1.41.1", features = ["rt-multi-thread", "macros", "time"] }
|
||||
twenty-twenty = "0.8"
|
||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
||||
|
||||
|
@ -12,4 +12,4 @@ kcl-lib = { version = "0.2", path = "../kcl" }
|
||||
pico-args = "0.5.0"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.128"
|
||||
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
|
||||
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread"] }
|
||||
|
@ -203,7 +203,7 @@ fn bad_gateway(msg: String) -> Response<Body> {
|
||||
resp
|
||||
}
|
||||
|
||||
fn kcl_err(err: anyhow::Error) -> Response<Body> {
|
||||
fn kcl_err(err: impl std::fmt::Display) -> Response<Body> {
|
||||
eprintln!("\tBad KCL");
|
||||
bad_gateway(format!("{err}"))
|
||||
}
|
||||
|
@ -19,5 +19,5 @@ indexmap = "2.6.0"
|
||||
kcl-lib = { path = "../kcl" }
|
||||
kittycad = { workspace = true, features = ["clap"] }
|
||||
kittycad-modeling-cmds = { workspace = true }
|
||||
tokio = { version = "1.38", features = ["full", "time", "rt", "tracing"] }
|
||||
tokio = { version = "1.41", features = ["full", "time", "rt", "tracing"] }
|
||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.25"
|
||||
version = "0.2.27"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -16,11 +16,15 @@ async-recursion = "1.1.1"
|
||||
async-trait = "0.1.83"
|
||||
base64 = "0.22.1"
|
||||
chrono = "0.4.38"
|
||||
clap = { version = "4.5.20", default-features = false, optional = true, features = ["std", "derive"] }
|
||||
clap = { version = "4.5.21", 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" }
|
||||
@ -33,30 +37,50 @@ kittycad = { workspace = true }
|
||||
kittycad-modeling-cmds = { workspace = true }
|
||||
lazy_static = "1.5.0"
|
||||
measurements = "0.11.0"
|
||||
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.40.0", features = ["sync", "time"] }
|
||||
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
|
||||
tokio = { version = "1.41.1", features = ["sync", "time"] }
|
||||
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"] }
|
||||
@ -64,13 +88,16 @@ web-sys = { version = "0.3.72", features = ["console"] }
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
approx = "0.5"
|
||||
bson = { version = "2.13.0", features = ["uuid-1", "chrono"] }
|
||||
tokio = { version = "1.39.3", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.24.0", features = ["rustls-tls-native-roots"] }
|
||||
tokio = { version = "1.41.1", features = ["full"] }
|
||||
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 = []
|
||||
@ -89,8 +116,9 @@ iai = "0.1"
|
||||
image = { version = "0.25.5", default-features = false, features = ["png"] }
|
||||
insta = { version = "1.41.1", features = ["json", "filters", "redactions"] }
|
||||
itertools = "0.13.0"
|
||||
miette = { version = "7.2.0", features = ["fancy"] }
|
||||
pretty_assertions = "1.4.1"
|
||||
tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
tokio = { version = "1.41.1", features = ["rt-multi-thread", "macros", "time"] }
|
||||
twenty-twenty = "0.8.0"
|
||||
|
||||
[[bench]]
|
||||
|
@ -8,17 +8,16 @@ use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use crate::{
|
||||
ast::types::{
|
||||
ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, PipeExpression, PipeSubstitution,
|
||||
VariableDeclarator,
|
||||
ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, Node, PipeExpression,
|
||||
PipeSubstitution, VariableDeclarator,
|
||||
},
|
||||
engine::EngineManager,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{Point2d, SourceRange},
|
||||
executor::Point2d,
|
||||
source_range::{ModuleId, SourceRange},
|
||||
Program,
|
||||
};
|
||||
|
||||
use super::types::{ModuleId, Node};
|
||||
|
||||
type Point3d = kcmc::shared::Point3d<f64>;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -27,8 +27,9 @@ pub use crate::ast::types::{
|
||||
use crate::{
|
||||
docs::StdLibFn,
|
||||
errors::KclError,
|
||||
executor::{ExecState, ExecutorContext, KclValue, Metadata, SourceRange, TagIdentifier},
|
||||
executor::{ExecState, ExecutorContext, KclValue, Metadata, TagIdentifier},
|
||||
parser::PIPE_OPERATOR,
|
||||
source_range::{ModuleId, SourceRange},
|
||||
std::kcl_stdlib::KclStdLibFn,
|
||||
};
|
||||
|
||||
@ -62,9 +63,13 @@ pub struct Node<T> {
|
||||
impl<T> Node<T> {
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
source_range: SourceRange([self.start, self.end, self.module_id.0 as usize]),
|
||||
source_range: SourceRange::new(self.start, self.end, self.module_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, pos: usize) -> bool {
|
||||
self.start <= pos && pos <= self.end
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JsonSchema> schemars::JsonSchema for Node<T> {
|
||||
@ -117,8 +122,12 @@ impl<T> Node<T> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_source_range(&self) -> SourceRange {
|
||||
SourceRange::new(self.start, self.end, self.module_id)
|
||||
}
|
||||
|
||||
pub fn as_source_ranges(&self) -> Vec<SourceRange> {
|
||||
vec![SourceRange([self.start, self.end, self.module_id.as_usize()])]
|
||||
vec![self.as_source_range()]
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,21 +151,21 @@ impl<T: fmt::Display> fmt::Display for Node<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Node<T>> for crate::executor::SourceRange {
|
||||
impl<T> From<Node<T>> for SourceRange {
|
||||
fn from(v: Node<T>) -> Self {
|
||||
Self([v.start, v.end, v.module_id.as_usize()])
|
||||
Self::new(v.start, v.end, v.module_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<&Node<T>> for crate::executor::SourceRange {
|
||||
impl<T> From<&Node<T>> for SourceRange {
|
||||
fn from(v: &Node<T>) -> Self {
|
||||
Self([v.start, v.end, v.module_id.as_usize()])
|
||||
Self::new(v.start, v.end, v.module_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<&BoxNode<T>> for crate::executor::SourceRange {
|
||||
impl<T> From<&BoxNode<T>> for SourceRange {
|
||||
fn from(v: &BoxNode<T>) -> Self {
|
||||
Self([v.start, v.end, v.module_id.as_usize()])
|
||||
Self::new(v.start, v.end, v.module_id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,6 +182,8 @@ pub struct Program {
|
||||
pub body: Vec<BodyItem>,
|
||||
#[serde(default, skip_serializing_if = "NonCodeMeta::is_empty")]
|
||||
pub non_code_meta: NonCodeMeta,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub shebang: Option<Node<Shebang>>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
@ -261,19 +272,14 @@ impl Program {
|
||||
}
|
||||
|
||||
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||
// Check if we are in the non code meta.
|
||||
if let Some(meta) = self.get_non_code_meta_for_position(pos) {
|
||||
for node in &meta.start_nodes {
|
||||
if node.contains(pos) {
|
||||
// We only care about the shebang.
|
||||
if let NonCodeValue::Shebang { value: _ } = &node.value {
|
||||
let source_range: SourceRange = node.into();
|
||||
return Some(Hover::Comment {
|
||||
value: r#"The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang)."#.to_string(),
|
||||
range: source_range.to_lsp_range(code),
|
||||
});
|
||||
}
|
||||
}
|
||||
// Check if we are in shebang.
|
||||
if let Some(node) = &self.shebang {
|
||||
if node.contains(pos) {
|
||||
let source_range: SourceRange = node.into();
|
||||
return Some(Hover::Comment {
|
||||
value: r#"The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang)."#.to_string(),
|
||||
range: source_range.to_lsp_range(code),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -528,26 +534,22 @@ impl Program {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
/// 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
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
#[ts(export)]
|
||||
pub struct ModuleId(pub u32);
|
||||
pub struct Shebang {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
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()
|
||||
impl Shebang {
|
||||
pub fn new(content: String) -> Self {
|
||||
Shebang { content }
|
||||
}
|
||||
}
|
||||
|
||||
@ -584,13 +586,13 @@ impl BodyItem {
|
||||
|
||||
impl From<BodyItem> for SourceRange {
|
||||
fn from(item: BodyItem) -> Self {
|
||||
Self([item.start(), item.end(), item.module_id().as_usize()])
|
||||
Self::new(item.start(), item.end(), item.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BodyItem> for SourceRange {
|
||||
fn from(item: &BodyItem) -> Self {
|
||||
Self([item.start(), item.end(), item.module_id().as_usize()])
|
||||
Self::new(item.start(), item.end(), item.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
@ -606,6 +608,7 @@ pub enum Expr {
|
||||
BinaryExpression(BoxNode<BinaryExpression>),
|
||||
FunctionExpression(BoxNode<FunctionExpression>),
|
||||
CallExpression(BoxNode<CallExpression>),
|
||||
CallExpressionKw(BoxNode<CallExpressionKw>),
|
||||
PipeExpression(BoxNode<PipeExpression>),
|
||||
PipeSubstitution(BoxNode<PipeSubstitution>),
|
||||
ArrayExpression(BoxNode<ArrayExpression>),
|
||||
@ -619,7 +622,7 @@ pub enum Expr {
|
||||
|
||||
impl Expr {
|
||||
pub fn get_lsp_folding_range(&self) -> Option<FoldingRange> {
|
||||
let recasted = self.recast(&FormatOptions::default(), 0, false);
|
||||
let recasted = self.recast(&FormatOptions::default(), 0, crate::unparser::ExprContext::Other);
|
||||
// If the code only has one line then we don't need to fold it.
|
||||
if recasted.lines().count() <= 1 {
|
||||
return None;
|
||||
@ -649,6 +652,7 @@ 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),
|
||||
@ -674,6 +678,7 @@ 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,6 +697,7 @@ 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,
|
||||
@ -712,6 +718,7 @@ 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,
|
||||
@ -733,6 +740,7 @@ 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),
|
||||
@ -761,6 +769,7 @@ 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),
|
||||
@ -787,6 +796,7 @@ 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()],
|
||||
@ -804,13 +814,13 @@ impl Expr {
|
||||
|
||||
impl From<Expr> for SourceRange {
|
||||
fn from(value: Expr) -> Self {
|
||||
Self([value.start(), value.end(), value.module_id().as_usize()])
|
||||
Self::new(value.start(), value.end(), value.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Expr> for SourceRange {
|
||||
fn from(value: &Expr) -> Self {
|
||||
Self([value.start(), value.end(), value.module_id().as_usize()])
|
||||
Self::new(value.start(), value.end(), value.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
@ -823,6 +833,7 @@ pub enum BinaryPart {
|
||||
Identifier(BoxNode<Identifier>),
|
||||
BinaryExpression(BoxNode<BinaryExpression>),
|
||||
CallExpression(BoxNode<CallExpression>),
|
||||
CallExpressionKw(BoxNode<CallExpressionKw>),
|
||||
UnaryExpression(BoxNode<UnaryExpression>),
|
||||
MemberExpression(BoxNode<MemberExpression>),
|
||||
IfExpression(BoxNode<IfExpression>),
|
||||
@ -830,13 +841,13 @@ pub enum BinaryPart {
|
||||
|
||||
impl From<BinaryPart> for SourceRange {
|
||||
fn from(value: BinaryPart) -> Self {
|
||||
Self([value.start(), value.end(), value.module_id().as_usize()])
|
||||
Self::new(value.start(), value.end(), value.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BinaryPart> for SourceRange {
|
||||
fn from(value: &BinaryPart) -> Self {
|
||||
Self([value.start(), value.end(), value.module_id().as_usize()])
|
||||
Self::new(value.start(), value.end(), value.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
@ -848,6 +859,7 @@ 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(),
|
||||
@ -864,6 +876,9 @@ 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)
|
||||
}
|
||||
@ -878,6 +893,7 @@ 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,
|
||||
@ -890,6 +906,7 @@ 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,
|
||||
@ -905,6 +922,7 @@ 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) => {
|
||||
@ -924,6 +942,9 @@ 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)
|
||||
}
|
||||
@ -948,13 +969,8 @@ pub struct NonCodeNode {
|
||||
}
|
||||
|
||||
impl Node<NonCodeNode> {
|
||||
pub fn contains(&self, pos: usize) -> bool {
|
||||
self.start <= pos && pos <= self.end
|
||||
}
|
||||
|
||||
pub fn format(&self, indentation: &str) -> String {
|
||||
match &self.value {
|
||||
NonCodeValue::Shebang { value } => format!("{}\n\n", value),
|
||||
NonCodeValue::InlineComment {
|
||||
value,
|
||||
style: CommentStyle::Line,
|
||||
@ -994,7 +1010,6 @@ impl Node<NonCodeNode> {
|
||||
impl NonCodeNode {
|
||||
pub fn value(&self) -> String {
|
||||
match &self.value {
|
||||
NonCodeValue::Shebang { value } => value.clone(),
|
||||
NonCodeValue::InlineComment { value, style: _ } => value.clone(),
|
||||
NonCodeValue::BlockComment { value, style: _ } => value.clone(),
|
||||
NonCodeValue::NewLineBlockComment { value, style: _ } => value.clone(),
|
||||
@ -1028,15 +1043,6 @@ impl CommentStyle {
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub enum NonCodeValue {
|
||||
/// 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
|
||||
/// ```
|
||||
Shebang {
|
||||
value: String,
|
||||
},
|
||||
/// An inline comment.
|
||||
/// Here are examples:
|
||||
/// `1 + 1 // This is an inline comment`.
|
||||
@ -1171,7 +1177,7 @@ impl Node<ImportItem> {
|
||||
self.alias = Some(Identifier::new(new_name));
|
||||
}
|
||||
// Return implicit name.
|
||||
return Some(self.identifier().to_owned());
|
||||
Some(self.identifier().to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1254,19 +1260,48 @@ 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 {
|
||||
@ -1286,12 +1321,30 @@ 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,
|
||||
}))
|
||||
}
|
||||
@ -1343,6 +1396,68 @@ 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)]
|
||||
@ -2191,13 +2306,13 @@ impl MemberObject {
|
||||
|
||||
impl From<MemberObject> for SourceRange {
|
||||
fn from(obj: MemberObject) -> Self {
|
||||
Self([obj.start(), obj.end(), obj.module_id().as_usize()])
|
||||
Self::new(obj.start(), obj.end(), obj.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&MemberObject> for SourceRange {
|
||||
fn from(obj: &MemberObject) -> Self {
|
||||
Self([obj.start(), obj.end(), obj.module_id().as_usize()])
|
||||
Self::new(obj.start(), obj.end(), obj.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
@ -2228,13 +2343,13 @@ impl LiteralIdentifier {
|
||||
|
||||
impl From<LiteralIdentifier> for SourceRange {
|
||||
fn from(id: LiteralIdentifier) -> Self {
|
||||
Self([id.start(), id.end(), id.module_id().as_usize()])
|
||||
Self::new(id.start(), id.end(), id.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&LiteralIdentifier> for SourceRange {
|
||||
fn from(id: &LiteralIdentifier) -> Self {
|
||||
Self([id.start(), id.end(), id.module_id().as_usize()])
|
||||
Self::new(id.start(), id.end(), id.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
@ -2820,7 +2935,6 @@ 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 {
|
||||
@ -3059,9 +3173,9 @@ ghi("things")
|
||||
folding_ranges[1].collapsed_text,
|
||||
Some("startSketchOn('XY')".to_string())
|
||||
);
|
||||
assert_eq!(folding_ranges[2].start_line, 390);
|
||||
assert_eq!(folding_ranges[2].start_line, 384);
|
||||
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]
|
||||
@ -3260,7 +3374,7 @@ 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();
|
||||
@ -3286,8 +3400,8 @@ const cylinder = startSketchOn('-XZ')
|
||||
name: "thing".to_owned(),
|
||||
digest: None
|
||||
},
|
||||
13,
|
||||
18,
|
||||
23,
|
||||
module_id,
|
||||
),
|
||||
type_: Some(FnArgType::Primitive(FnArgPrimitive::Number)),
|
||||
@ -3300,8 +3414,8 @@ const cylinder = startSketchOn('-XZ')
|
||||
name: "things".to_owned(),
|
||||
digest: None
|
||||
},
|
||||
33,
|
||||
39,
|
||||
28,
|
||||
34,
|
||||
module_id,
|
||||
),
|
||||
type_: Some(FnArgType::Array(FnArgPrimitive::String)),
|
||||
@ -3314,8 +3428,8 @@ const cylinder = startSketchOn('-XZ')
|
||||
name: "more".to_owned(),
|
||||
digest: None
|
||||
},
|
||||
51,
|
||||
55,
|
||||
46,
|
||||
50,
|
||||
module_id,
|
||||
),
|
||||
type_: Some(FnArgType::Primitive(FnArgPrimitive::String)),
|
||||
@ -3338,6 +3452,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
body: Node::no_src(Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
}),
|
||||
return_type: None,
|
||||
@ -3361,6 +3476,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
inner: Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
@ -3388,6 +3504,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
inner: Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
@ -3426,6 +3543,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
inner: Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
|
@ -1,15 +1,10 @@
|
||||
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
|
||||
@ -50,7 +45,7 @@ impl Node<IfExpression> {
|
||||
impl Node<ElseIf> {
|
||||
#[allow(dead_code)]
|
||||
fn source_ranges(&self) -> Vec<SourceRange> {
|
||||
vec![SourceRange([self.start, self.end, self.module_id.as_usize()])]
|
||||
vec![SourceRange::new(self.start, self.end, self.module_id)]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
use sha2::{Digest as DigestTrait, Sha256};
|
||||
|
||||
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,
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, CallExpressionKw,
|
||||
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.
|
||||
@ -66,6 +66,9 @@ impl Program {
|
||||
for body_item in slf.body.iter_mut() {
|
||||
hasher.update(body_item.compute_digest());
|
||||
}
|
||||
if let Some(shebang) = &slf.shebang {
|
||||
hasher.update(&shebang.inner.content);
|
||||
}
|
||||
hasher.update(slf.non_code_meta.compute_digest());
|
||||
});
|
||||
}
|
||||
@ -90,6 +93,7 @@ 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(),
|
||||
@ -114,6 +118,7 @@ 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(),
|
||||
@ -207,9 +212,6 @@ impl ReturnStatement {
|
||||
impl NonCodeNode {
|
||||
compute_digest!(|slf, hasher| {
|
||||
match &slf.value {
|
||||
NonCodeValue::Shebang { value } => {
|
||||
hasher.update(value);
|
||||
}
|
||||
NonCodeValue::InlineComment { value, style } => {
|
||||
hasher.update(value);
|
||||
hasher.update(style.digestable_id());
|
||||
@ -369,7 +371,22 @@ impl CallExpression {
|
||||
for argument in slf.arguments.iter_mut() {
|
||||
hasher.update(argument.compute_digest());
|
||||
}
|
||||
hasher.update(if slf.optional { [1] } else { [0] });
|
||||
});
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use async_recursion::async_recursion;
|
||||
|
||||
use super::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, CallExpression, Expr,
|
||||
IfExpression, KclNone, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, ObjectExpression,
|
||||
TagDeclarator, UnaryExpression, UnaryOperator,
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, CallExpression,
|
||||
CallExpressionKw, Expr, IfExpression, KclNone, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject,
|
||||
Node, ObjectExpression, TagDeclarator, UnaryExpression, UnaryOperator,
|
||||
};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{
|
||||
BodyType, ExecState, ExecutorContext, KclValue, Metadata, SourceRange, StatementKind, TagEngineInfo,
|
||||
TagIdentifier,
|
||||
},
|
||||
executor::{BodyType, ExecState, ExecutorContext, KclValue, Metadata, StatementKind, TagEngineInfo, TagIdentifier},
|
||||
source_range::SourceRange,
|
||||
std::{args::Arg, FunctionKind},
|
||||
};
|
||||
use async_recursion::async_recursion;
|
||||
|
||||
const FLOAT_TO_INT_MAX_DELTA: f64 = 0.01;
|
||||
|
||||
@ -28,6 +27,7 @@ impl BinaryPart {
|
||||
}
|
||||
BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
|
||||
BinaryPart::CallExpression(call_expression) => call_expression.execute(exec_state, ctx).await,
|
||||
BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
|
||||
BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
|
||||
@ -333,6 +333,7 @@ async fn inner_execute_pipe_body(
|
||||
| Expr::BinaryExpression(_)
|
||||
| Expr::FunctionExpression(_)
|
||||
| Expr::CallExpression(_)
|
||||
| Expr::CallExpressionKw(_)
|
||||
| Expr::PipeExpression(_)
|
||||
| Expr::PipeSubstitution(_)
|
||||
| Expr::ArrayExpression(_)
|
||||
@ -356,6 +357,12 @@ async fn inner_execute_pipe_body(
|
||||
Ok(final_output)
|
||||
}
|
||||
|
||||
impl Node<CallExpressionKw> {
|
||||
pub async fn execute(&self, _exec_state: &mut ExecState, _ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<CallExpression> {
|
||||
#[async_recursion]
|
||||
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
|
@ -3,9 +3,8 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JValue;
|
||||
|
||||
use crate::ast::types::{Expr, Literal};
|
||||
|
||||
use super::Node;
|
||||
use crate::ast::types::{Expr, Literal};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
|
@ -4,9 +4,8 @@ use databake::*;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ast::types::ConstraintLevel, executor::KclValue};
|
||||
|
||||
use super::Node;
|
||||
use crate::{ast::types::ConstraintLevel, executor::KclValue};
|
||||
|
||||
const KCL_NONE_ID: &str = "KCL_NONE_ID";
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject, ModuleId};
|
||||
use super::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject};
|
||||
use crate::source_range::ModuleId;
|
||||
|
||||
impl BodyItem {
|
||||
pub fn module_id(&self) -> ModuleId {
|
||||
@ -20,6 +21,7 @@ impl Expr {
|
||||
Expr::BinaryExpression(binary_expression) => binary_expression.module_id,
|
||||
Expr::FunctionExpression(function_expression) => function_expression.module_id,
|
||||
Expr::CallExpression(call_expression) => call_expression.module_id,
|
||||
Expr::CallExpressionKw(call_expression) => call_expression.module_id,
|
||||
Expr::PipeExpression(pipe_expression) => pipe_expression.module_id,
|
||||
Expr::PipeSubstitution(pipe_substitution) => pipe_substitution.module_id,
|
||||
Expr::ArrayExpression(array_expression) => array_expression.module_id,
|
||||
@ -40,6 +42,7 @@ impl BinaryPart {
|
||||
BinaryPart::Identifier(identifier) => identifier.module_id,
|
||||
BinaryPart::BinaryExpression(binary_expression) => binary_expression.module_id,
|
||||
BinaryPart::CallExpression(call_expression) => call_expression.module_id,
|
||||
BinaryPart::CallExpressionKw(call_expression) => call_expression.module_id,
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.module_id,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.module_id,
|
||||
BinaryPart::IfExpression(e) => e.module_id,
|
||||
|