Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
b17e61d963 | |||
d31d07d9c8 | |||
7aa2d63c21 | |||
e1081b0ee6 | |||
59223279b7 | |||
8a4e717565 | |||
80b542ca18 | |||
e4bfc863ea | |||
77ef255de4 | |||
64c3841079 | |||
c7bb6bc845 | |||
1af8a8c64f | |||
eb4776826b | |||
f3dd0469d5 | |||
deea74754d | |||
3fd798c704 | |||
cc9eaf2991 | |||
6f24031220 | |||
672bcd297f | |||
3bc182fe16 | |||
589cd39eec | |||
63feebef5c | |||
65037abd9a | |||
97bc339a62 | |||
4e9a6375a5 | |||
3d19dfb800 | |||
d2a7b84292 | |||
9e02bab155 | |||
7352de5a70 | |||
9797d0cb81 | |||
83907fa9db | |||
a367be4e2b | |||
056fa00adc | |||
4759fb2e6f | |||
45f497d9cd | |||
dc61bdebdf | |||
61943055e5 | |||
416fe0f644 | |||
708465d818 | |||
e706fb02d6 | |||
1bf7daa474 | |||
ffc47f8f40 | |||
768aaa84f6 | |||
f3a700eec8 | |||
c853637a9a | |||
9af30d9ef6 | |||
6164714a6b | |||
64ceb98eba | |||
2cbf260900 | |||
cfaaedf602 | |||
12b3717eb5 | |||
0bc685b0c4 | |||
9ee032771a | |||
c307ddd1b1 | |||
a30818ff2b | |||
53e763d938 | |||
8f74cd1d0c | |||
c271942897 | |||
a03d09b41d | |||
2971b7752b | |||
70e99eb00b | |||
5c66af59d2 | |||
6dda6daeef |
3
.github/workflows/cargo-build.yml
vendored
3
.github/workflows/cargo-build.yml
vendored
@ -15,6 +15,9 @@ on:
|
||||
- '**/Cargo.lock'
|
||||
- '**/rust-toolchain.toml'
|
||||
- .github/workflows/cargo-build.yml
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo build
|
||||
jobs:
|
||||
cargobuild:
|
||||
|
3
.github/workflows/cargo-clippy.yml
vendored
3
.github/workflows/cargo-clippy.yml
vendored
@ -15,6 +15,9 @@ on:
|
||||
- '**/rust-toolchain.toml'
|
||||
- '**.rs'
|
||||
- .github/workflows/cargo-build.yml
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo clippy
|
||||
jobs:
|
||||
cargoclippy:
|
||||
|
3
.github/workflows/cargo-criterion.yml
vendored
3
.github/workflows/cargo-criterion.yml
vendored
@ -17,6 +17,9 @@ on:
|
||||
- .github/workflows/cargo-criterion.yml
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo criterion
|
||||
jobs:
|
||||
cargocriterion:
|
||||
|
3
.github/workflows/cargo-fmt.yml
vendored
3
.github/workflows/cargo-fmt.yml
vendored
@ -18,6 +18,9 @@ on:
|
||||
permissions:
|
||||
packages: read
|
||||
contents: read
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo fmt
|
||||
jobs:
|
||||
cargofmt:
|
||||
|
3
.github/workflows/cargo-test.yml
vendored
3
.github/workflows/cargo-test.yml
vendored
@ -17,6 +17,9 @@ on:
|
||||
- .github/workflows/cargo-test.yml
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo test
|
||||
jobs:
|
||||
cargotest:
|
||||
|
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -8,6 +8,9 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
check-format:
|
||||
runs-on: 'ubuntu-20.04'
|
||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 The KittyCAD Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
3121
docs/kcl/std.json
3121
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
630
docs/kcl/std.md
630
docs/kcl/std.md
@ -48,8 +48,12 @@
|
||||
* [`show`](#show)
|
||||
* [`sin`](#sin)
|
||||
* [`sqrt`](#sqrt)
|
||||
* [`startProfileAt`](#startProfileAt)
|
||||
* [`startSketchAt`](#startSketchAt)
|
||||
* [`startSketchOn`](#startSketchOn)
|
||||
* [`tan`](#tan)
|
||||
* [`tangentalArc`](#tangentalArc)
|
||||
* [`tangentalArcTo`](#tangentalArcTo)
|
||||
* [`tau`](#tau)
|
||||
* [`xLine`](#xLine)
|
||||
* [`xLineTo`](#xLineTo)
|
||||
@ -118,6 +122,8 @@ angleToMatchLengthX(segment_name: string, to: number, sketch_group: SketchGroup)
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -202,6 +208,8 @@ angleToMatchLengthY(segment_name: string, to: number, sketch_group: SketchGroup)
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -296,6 +304,8 @@ angledLine(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -362,6 +372,8 @@ angledLine(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -452,6 +464,8 @@ angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -518,6 +532,8 @@ angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -608,6 +624,8 @@ angledLineOfYLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -674,6 +692,8 @@ angledLineOfYLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -765,6 +785,8 @@ angledLineThatIntersects(data: AngeledLineThatIntersectsData, sketch_group: Sket
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -831,6 +853,8 @@ angledLineThatIntersects(data: AngeledLineThatIntersectsData, sketch_group: Sket
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -921,6 +945,8 @@ angledLineToX(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -987,6 +1013,8 @@ angledLineToX(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1077,6 +1105,8 @@ angledLineToY(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1143,6 +1173,8 @@ angledLineToY(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1260,6 +1292,8 @@ arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1326,6 +1360,8 @@ arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1465,6 +1501,8 @@ bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1531,6 +1569,8 @@ bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1629,6 +1669,8 @@ close(sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1695,6 +1737,8 @@ close(sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1813,6 +1857,8 @@ extrude(length: number, sketch_group: SketchGroup) -> ExtrudeGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1994,6 +2040,8 @@ lastSegX(sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2076,6 +2124,8 @@ lastSegY(sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2231,6 +2281,8 @@ line(data: LineData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2297,6 +2349,8 @@ line(data: LineData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2385,6 +2439,8 @@ lineTo(data: LineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2451,6 +2507,8 @@ lineTo(data: LineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2691,6 +2749,8 @@ segAng(segment_name: string, sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2774,6 +2834,8 @@ segEndX(segment_name: string, sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2857,6 +2919,8 @@ segEndY(segment_name: string, sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2940,6 +3004,8 @@ segLen(segment_name: string, sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3022,6 +3088,8 @@ show(sketch: SketchGroup)
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3123,14 +3191,14 @@ sqrt(num: number) -> number
|
||||
|
||||
|
||||
|
||||
### startSketchAt
|
||||
### startProfileAt
|
||||
|
||||
Start a sketch at a given point.
|
||||
Start a profile at a given point.
|
||||
|
||||
|
||||
|
||||
```
|
||||
startSketchAt(data: LineData) -> SketchGroup
|
||||
startProfileAt(data: LineData, plane: Plane) -> SketchGroup
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
@ -3145,6 +3213,40 @@ startSketchAt(data: LineData) -> SketchGroup
|
||||
} |
|
||||
[number]
|
||||
```
|
||||
* `plane`: `Plane` - A plane.
|
||||
```
|
||||
{
|
||||
// The id of the plane.
|
||||
id: uuid,
|
||||
// Origin of the plane.
|
||||
origin: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// Type for a plane.
|
||||
value: string |
|
||||
string,
|
||||
// What should the plane’s X axis be?
|
||||
xAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// What should the plane’s Y axis be?
|
||||
yAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// The z-axis (normal).
|
||||
zAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
@ -3153,6 +3255,8 @@ startSketchAt(data: LineData) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3214,6 +3318,188 @@ startSketchAt(data: LineData) -> SketchGroup
|
||||
|
||||
|
||||
|
||||
### startSketchAt
|
||||
|
||||
Start a sketch at a given point on the 'XY' plane.
|
||||
|
||||
|
||||
|
||||
```
|
||||
startSketchAt(data: LineData) -> SketchGroup
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
* `data`: `LineData` - Data to draw a line.
|
||||
```
|
||||
{
|
||||
// The tag.
|
||||
tag: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
} |
|
||||
[number]
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
* `SketchGroup` - A sketch group is a collection of paths.
|
||||
```
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
rotation: [number],
|
||||
// The starting path.
|
||||
start: {
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
// The y coordinate.
|
||||
y: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
}],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### startSketchOn
|
||||
|
||||
Start a sketch at a given point.
|
||||
|
||||
|
||||
|
||||
```
|
||||
startSketchOn(data: PlaneData) -> Plane
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
* `data`: `PlaneData` - Data for a plane.
|
||||
```
|
||||
string |
|
||||
string |
|
||||
string |
|
||||
string |
|
||||
string |
|
||||
string |
|
||||
{
|
||||
plane: {
|
||||
// Origin of the plane.
|
||||
origin: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// What should the plane’s X axis be?
|
||||
x_axis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// What should the plane’s Y axis be?
|
||||
y_axis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// The z-axis (normal).
|
||||
z_axis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
* `Plane` - A plane.
|
||||
```
|
||||
{
|
||||
// The id of the plane.
|
||||
id: uuid,
|
||||
// Origin of the plane.
|
||||
origin: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// Type for a plane.
|
||||
value: string |
|
||||
string,
|
||||
// What should the plane’s X axis be?
|
||||
xAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// What should the plane’s Y axis be?
|
||||
yAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// The z-axis (normal).
|
||||
zAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### tan
|
||||
|
||||
Computes the tangent of a number (in radians).
|
||||
@ -3234,6 +3520,328 @@ tan(num: number) -> number
|
||||
|
||||
|
||||
|
||||
### tangentalArc
|
||||
|
||||
Draw an arc.
|
||||
|
||||
|
||||
|
||||
```
|
||||
tangentalArc(data: TangentalArcData, sketch_group: SketchGroup) -> SketchGroup
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
* `data`: `TangentalArcData` - Data to draw a tangental arc.
|
||||
```
|
||||
{
|
||||
// Offset of the arc, in degrees.
|
||||
offset: number,
|
||||
// Radius of the arc. Not to be confused with Raiders of the Lost Ark.
|
||||
radius: number,
|
||||
} |
|
||||
{
|
||||
// The tag.
|
||||
tag: string,
|
||||
// Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
|
||||
to: [number],
|
||||
} |
|
||||
[number]
|
||||
```
|
||||
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
|
||||
```
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
rotation: [number],
|
||||
// The starting path.
|
||||
start: {
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
// The y coordinate.
|
||||
y: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
}],
|
||||
}
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
* `SketchGroup` - A sketch group is a collection of paths.
|
||||
```
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
rotation: [number],
|
||||
// The starting path.
|
||||
start: {
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
// The y coordinate.
|
||||
y: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
}],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### tangentalArcTo
|
||||
|
||||
Draw an arc.
|
||||
|
||||
|
||||
|
||||
```
|
||||
tangentalArcTo(data: TangentalArcToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
* `data`: `TangentalArcToData` - Data to draw a tangental arc to a specific point.
|
||||
```
|
||||
{
|
||||
// The tag.
|
||||
tag: string,
|
||||
// Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
|
||||
to: [number],
|
||||
} |
|
||||
[number]
|
||||
```
|
||||
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
|
||||
```
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
rotation: [number],
|
||||
// The starting path.
|
||||
start: {
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
// The y coordinate.
|
||||
y: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
}],
|
||||
}
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
* `SketchGroup` - A sketch group is a collection of paths.
|
||||
```
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
rotation: [number],
|
||||
// The starting path.
|
||||
start: {
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
// The y coordinate.
|
||||
y: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
}],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### tau
|
||||
|
||||
Return the value of `tau`. The full circle constant (τ). Equal to 2π.
|
||||
@ -3280,6 +3888,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3346,6 +3956,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3434,6 +4046,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3500,6 +4114,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3588,6 +4204,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3654,6 +4272,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3742,6 +4362,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3808,6 +4430,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
|
11
package.json
11
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.9.1",
|
||||
"version": "0.10.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.9.0",
|
||||
@ -10,7 +10,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "^0.0.38",
|
||||
"@kittycad/lib": "^0.0.43",
|
||||
"@lezer/javascript": "^1.4.7",
|
||||
"@open-rpc/client-js": "^1.8.1",
|
||||
"@react-hook/resize-observer": "^1.2.6",
|
||||
@ -32,7 +32,7 @@
|
||||
"fuse.js": "^6.6.2",
|
||||
"http-server": "^14.1.1",
|
||||
"json-rpc-2.0": "^1.6.0",
|
||||
"re-resizable": "^6.9.9",
|
||||
"re-resizable": "^6.9.11",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
@ -48,7 +48,7 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.4.2",
|
||||
"uuid": "^9.0.0",
|
||||
"vitest": "^0.34.1",
|
||||
"vitest": "^0.34.6",
|
||||
"vscode-jsonrpc": "^8.1.0",
|
||||
"vscode-languageserver-protocol": "^3.17.3",
|
||||
"wasm-pack": "^0.12.1",
|
||||
@ -102,7 +102,6 @@
|
||||
"@babel/preset-env": "^7.22.9",
|
||||
"@tauri-apps/cli": "^1.3.1",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/debounce": "^1.2.1",
|
||||
"@types/debounce-promise": "^3.1.6",
|
||||
"@types/isomorphic-fetch": "^0.0.36",
|
||||
"@types/react-modal": "^3.16.0",
|
||||
@ -117,7 +116,7 @@
|
||||
"eslint-plugin-css-modules": "^2.11.0",
|
||||
"happy-dom": "^10.8.0",
|
||||
"husky": "^8.0.3",
|
||||
"postcss": "^8.4.19",
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^2.8.0",
|
||||
"setimmediate": "^1.0.5",
|
||||
"tailwindcss": "^3.2.4",
|
||||
|
122
src-tauri/Cargo.lock
generated
122
src-tauri/Cargo.lock
generated
@ -84,7 +84,7 @@ dependencies = [
|
||||
"tauri-build",
|
||||
"tauri-plugin-fs-extra",
|
||||
"tokio",
|
||||
"toml 0.8.0",
|
||||
"toml 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1309,7 +1309,21 @@ checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"markup5ever 0.10.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever 0.11.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
@ -1644,9 +1658,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.2.26"
|
||||
version = "0.2.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2623ee601ce203476229df3f9d3a14664cb43e3f7455e9ac8ed91aacaa6163d"
|
||||
checksum = "35b2f9302648dbb06fd7121687f9505fc3179eba84111a06d76b246e3158f5dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1686,7 +1700,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever",
|
||||
"html5ever 0.25.2",
|
||||
"matches",
|
||||
"selectors",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kuchikiki"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever 0.26.0",
|
||||
"indexmap 1.9.3",
|
||||
"matches",
|
||||
"selectors",
|
||||
]
|
||||
@ -1748,9 +1775,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.18"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -1802,7 +1829,21 @@ checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen",
|
||||
"phf_codegen 0.8.0",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf 0.10.1",
|
||||
"phf_codegen 0.10.0",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
"tendril",
|
||||
@ -2374,6 +2415,16 @@ dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||
dependencies = [
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
@ -3139,7 +3190,7 @@ dependencies = [
|
||||
"log",
|
||||
"matches",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen",
|
||||
"phf_codegen 0.8.0",
|
||||
"precomputed-hash",
|
||||
"servo_arc",
|
||||
"smallvec",
|
||||
@ -3661,9 +3712,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "1.4.1"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e"
|
||||
checksum = "0238c5063bf9613054149a1b6bce4935922e532b7d8211f36989a490a79806be"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.2",
|
||||
@ -3717,12 +3768,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b"
|
||||
checksum = "defbfc551bd38ab997e5f8e458f87396d2559d05ce32095076ad6c30f7fc5f9c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
"dirs-next",
|
||||
"heck 0.4.1",
|
||||
"json-patch",
|
||||
"semver",
|
||||
@ -3730,13 +3782,14 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"tauri-winres",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a"
|
||||
checksum = "7b3475e55acec0b4a50fb96435f19631fb58cbcd31923e1a213de5c382536bbb"
|
||||
dependencies = [
|
||||
"base64 0.21.2",
|
||||
"brotli",
|
||||
@ -3760,9 +3813,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7"
|
||||
checksum = "613740228de92d9196b795ac455091d3a5fbdac2654abb8bb07d010b62ab43af"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
@ -3775,7 +3828,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs-extra"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#0190f68f1dff80576595a1b79e31338a3e9ebba1"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#fa32d1afa97f52f74d814c5619b8d95da3268e3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@ -3786,9 +3839,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769"
|
||||
checksum = "07f8e9e53e00e9f41212c115749e87d5cd2a9eebccafca77a19722eeecd56d43"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
@ -3807,9 +3860,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b"
|
||||
checksum = "8141d72b6b65f2008911e9ef5b98a68d1e3413b7a1464e8f85eb3673bb19a895"
|
||||
dependencies = [
|
||||
"cocoa",
|
||||
"gtk",
|
||||
@ -3827,19 +3880,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84"
|
||||
checksum = "34d55e185904a84a419308d523c2c6891d5e2dbcee740c4997eb42e75a7b0f46"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"ctor",
|
||||
"dunce",
|
||||
"glob",
|
||||
"heck 0.4.1",
|
||||
"html5ever",
|
||||
"html5ever 0.26.0",
|
||||
"infer",
|
||||
"json-patch",
|
||||
"kuchiki",
|
||||
"kuchikiki",
|
||||
"log",
|
||||
"memchr",
|
||||
"phf 0.10.1",
|
||||
"proc-macro2",
|
||||
@ -4024,14 +4078,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.20.0",
|
||||
"toml_edit 0.20.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4058,9 +4112,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.20.0"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
dependencies = [
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
@ -4855,9 +4909,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.24.3"
|
||||
version = "0.24.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33748f35413c8a98d45f7a08832d848c0c5915501803d1faade5a4ebcd258cea"
|
||||
checksum = "88ef04bdad49eba2e01f06e53688c8413bd6a87b0bc14b72284465cf96e3578e"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"block",
|
||||
@ -4869,7 +4923,7 @@ dependencies = [
|
||||
"gio",
|
||||
"glib",
|
||||
"gtk",
|
||||
"html5ever",
|
||||
"html5ever 0.25.2",
|
||||
"http",
|
||||
"kuchiki",
|
||||
"libc",
|
||||
|
@ -12,18 +12,18 @@ rust-version = "1.60"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.4.0", features = [] }
|
||||
tauri-build = { version = "1.5.0", features = [] }
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
kittycad = "0.2.26"
|
||||
kittycad = "0.2.28"
|
||||
oauth2 = "4.4.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tauri = { version = "1.4.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
|
||||
tauri = { version = "1.5.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
|
||||
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
tokio = { version = "1.32.0", features = ["time"] }
|
||||
toml = "0.8.0"
|
||||
toml = "0.8.2"
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
|
||||
|
@ -8,7 +8,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "kittycad-modeling",
|
||||
"version": "0.9.1"
|
||||
"version": "0.10.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
@ -1,9 +1,15 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { App } from './App'
|
||||
import { describe, test, vi } from 'vitest'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import {
|
||||
Route,
|
||||
RouterProvider,
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
} from 'react-router-dom'
|
||||
import { GlobalStateProvider } from './components/GlobalStateProvider'
|
||||
import CommandBarProvider from 'components/CommandBar'
|
||||
import { BROWSER_FILE_NAME } from 'Router'
|
||||
|
||||
let listener: ((rect: any) => void) | undefined = undefined
|
||||
;(global as any).ResizeObserver = class ResizeObserver {
|
||||
@ -24,7 +30,7 @@ describe('App tests', () => {
|
||||
>
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ id: 'new' }),
|
||||
useParams: () => ({ id: BROWSER_FILE_NAME }),
|
||||
useLoaderData: () => ({ code: null }),
|
||||
}
|
||||
})
|
||||
@ -41,12 +47,24 @@ describe('App tests', () => {
|
||||
})
|
||||
|
||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||
// wrap in router and xState context
|
||||
return (
|
||||
<BrowserRouter>
|
||||
// We have to use a memory router in the testing environment,
|
||||
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
|
||||
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
|
||||
const router = createMemoryRouter(
|
||||
createRoutesFromElements(
|
||||
<Route
|
||||
path="/file/:id"
|
||||
element={
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>{children}</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
{
|
||||
initialEntries: ['/file/new'],
|
||||
initialIndex: 0,
|
||||
}
|
||||
)
|
||||
return <RouterProvider router={router} />
|
||||
}
|
||||
|
12
src/App.tsx
12
src/App.tsx
@ -31,6 +31,7 @@ import { TextEditor } from 'components/TextEditor'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
||||
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
||||
import { engineCommandManager } from './lang/std/engineConnection'
|
||||
|
||||
export function App() {
|
||||
const { code: loadedCode, project } = useLoaderData() as IndexLoaderData
|
||||
@ -39,7 +40,6 @@ export function App() {
|
||||
useHotKeyListener()
|
||||
const {
|
||||
setCode,
|
||||
engineCommandManager,
|
||||
buttonDownInStream,
|
||||
openPanes,
|
||||
setOpenPanes,
|
||||
@ -52,7 +52,6 @@ export function App() {
|
||||
guiMode: s.guiMode,
|
||||
setGuiMode: s.setGuiMode,
|
||||
setCode: s.setCode,
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
buttonDownInStream: s.buttonDownInStream,
|
||||
openPanes: s.openPanes,
|
||||
setOpenPanes: s.setOpenPanes,
|
||||
@ -91,12 +90,12 @@ export function App() {
|
||||
if (guiMode.sketchMode === 'sketchEdit') {
|
||||
// TODO: share this with Toolbar's "Exit sketch" button
|
||||
// exiting sketch should be done consistently across all exits
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'edit_mode_exit' },
|
||||
})
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'default_camera_disable_sketch_mode' },
|
||||
@ -107,7 +106,7 @@ export function App() {
|
||||
// when exiting sketch mode in the future
|
||||
executeAst()
|
||||
} else {
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
@ -156,7 +155,7 @@ export function App() {
|
||||
useEngineConnectionSubscriptions()
|
||||
|
||||
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
||||
engineCommandManager?.sendSceneCommand(message)
|
||||
engineCommandManager.sendSceneCommand(message)
|
||||
}, 16)
|
||||
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
e.nativeEvent.preventDefault()
|
||||
@ -216,7 +215,6 @@ export function App() {
|
||||
} else if (interactionGuards.zoom.dragCallback(eWithButton)) {
|
||||
interaction = 'zoom'
|
||||
} else {
|
||||
console.log('none')
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -94,6 +94,8 @@ export const paths = {
|
||||
) as typeof onboardingPaths,
|
||||
}
|
||||
|
||||
export const BROWSER_FILE_NAME = 'new'
|
||||
|
||||
export type IndexLoaderData = {
|
||||
code: string | null
|
||||
project?: ProjectWithEntryPointMetadata
|
||||
@ -129,7 +131,9 @@ const router = createBrowserRouter(
|
||||
{
|
||||
path: paths.INDEX,
|
||||
loader: () =>
|
||||
isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'),
|
||||
isTauri()
|
||||
? redirect(paths.HOME)
|
||||
: redirect(paths.FILE + '/' + BROWSER_FILE_NAME),
|
||||
errorElement: <ErrorPage />,
|
||||
},
|
||||
{
|
||||
@ -167,7 +171,7 @@ const router = createBrowserRouter(
|
||||
)
|
||||
}
|
||||
|
||||
if (params.id && params.id !== 'new') {
|
||||
if (params.id && params.id !== BROWSER_FILE_NAME) {
|
||||
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
||||
const code = await readTextFile(params.id + '/' + PROJECT_ENTRYPOINT)
|
||||
const entrypoint_metadata = await metadata(
|
||||
@ -212,7 +216,7 @@ const router = createBrowserRouter(
|
||||
),
|
||||
loader: async () => {
|
||||
if (!isTauri()) {
|
||||
return redirect(paths.FILE + '/new')
|
||||
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
|
||||
}
|
||||
const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY)
|
||||
const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial<
|
||||
|
@ -10,7 +10,7 @@ import { SetHorzVertDistance } from './components/Toolbar/SetHorzVertDistance'
|
||||
import { SetAngleLength } from './components/Toolbar/setAngleLength'
|
||||
import { SetAbsDistance } from './components/Toolbar/SetAbsDistance'
|
||||
import { SetAngleBetween } from './components/Toolbar/SetAngleBetween'
|
||||
import { Fragment, useEffect } from 'react'
|
||||
import { Fragment, WheelEvent, useRef } from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faSearch, faX } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
@ -18,6 +18,7 @@ import styles from './Toolbar.module.css'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useAppMode } from 'hooks/useAppMode'
|
||||
import { ActionIcon } from 'components/ActionIcon'
|
||||
import { engineCommandManager } from './lang/std/engineConnection'
|
||||
|
||||
export const sketchButtonClassnames = {
|
||||
background:
|
||||
@ -50,7 +51,6 @@ export const Toolbar = () => {
|
||||
ast,
|
||||
updateAst,
|
||||
programMemory,
|
||||
engineCommandManager,
|
||||
executeAst,
|
||||
} = useStore((s) => ({
|
||||
guiMode: s.guiMode,
|
||||
@ -59,18 +59,27 @@ export const Toolbar = () => {
|
||||
ast: s.ast,
|
||||
updateAst: s.updateAst,
|
||||
programMemory: s.programMemory,
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
executeAst: s.executeAst,
|
||||
}))
|
||||
useAppMode()
|
||||
const toolbarButtonsRef = useRef<HTMLSpanElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
console.log('guiMode', guiMode)
|
||||
}, [guiMode])
|
||||
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
|
||||
const span = toolbarButtonsRef.current
|
||||
if (!span) {
|
||||
return
|
||||
}
|
||||
|
||||
span.scrollLeft = span.scrollLeft += ev.deltaY
|
||||
}
|
||||
|
||||
function ToolbarButtons({ className }: React.HTMLAttributes<HTMLElement>) {
|
||||
return (
|
||||
<span className={styles.toolbarButtons + ' ' + className}>
|
||||
<span
|
||||
ref={toolbarButtonsRef}
|
||||
onWheel={handleToolbarButtonsWheelEvent}
|
||||
className={styles.toolbarButtons + ' ' + className}
|
||||
>
|
||||
{guiMode.mode === 'default' && (
|
||||
<button
|
||||
onClick={() => {
|
||||
@ -173,12 +182,12 @@ export const Toolbar = () => {
|
||||
{guiMode.mode === 'sketch' && (
|
||||
<button
|
||||
onClick={() => {
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'edit_mode_exit' },
|
||||
})
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'default_camera_disable_sketch_mode' },
|
||||
@ -214,7 +223,7 @@ export const Toolbar = () => {
|
||||
<button
|
||||
key={sketchFnName}
|
||||
onClick={() => {
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
|
@ -23,10 +23,7 @@ type ActionButtonAsLink = BaseActionButtonProps &
|
||||
}
|
||||
|
||||
type ActionButtonAsExternal = BaseActionButtonProps &
|
||||
Omit<
|
||||
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||
keyof BaseActionButtonProps
|
||||
> & {
|
||||
Omit<LinkProps, keyof BaseActionButtonProps> & {
|
||||
Element: 'externalLink'
|
||||
}
|
||||
|
||||
@ -69,12 +66,17 @@ export const ActionButton = (props: ActionButtonProps) => {
|
||||
)
|
||||
}
|
||||
case 'externalLink': {
|
||||
const { Element, icon, children, className, ...rest } = props
|
||||
const { Element, to, icon, children, className, ...rest } = props
|
||||
return (
|
||||
<a className={classNames} {...rest}>
|
||||
<Link
|
||||
to={to || paths.INDEX}
|
||||
className={classNames}
|
||||
{...rest}
|
||||
target="_blank"
|
||||
>
|
||||
{icon && <ActionIcon {...icon} />}
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
default: {
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { parser_wasm } from '../lang/abstractSyntaxTree'
|
||||
import { BinaryPart, Value } from '../lang/abstractSyntaxTreeTypes'
|
||||
import { executor } from '../lang/executor'
|
||||
import { parse, BinaryPart, Value, executor } from '../lang/wasm'
|
||||
import {
|
||||
createIdentifier,
|
||||
createLiteral,
|
||||
@ -10,6 +8,7 @@ import {
|
||||
} from '../lang/modifyAst'
|
||||
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
|
||||
import { useStore } from '../useStore'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
|
||||
export const AvailableVars = ({
|
||||
onVarClick,
|
||||
@ -92,12 +91,12 @@ export function useCalc({
|
||||
newVariableInsertIndex: number
|
||||
setNewVariableName: (a: string) => void
|
||||
} {
|
||||
const { ast, programMemory, selectionRange, engineCommandManager } = useStore(
|
||||
const { ast, programMemory, selectionRange, defaultPlanes } = useStore(
|
||||
(s) => ({
|
||||
ast: s.ast,
|
||||
programMemory: s.programMemory,
|
||||
selectionRange: s.selectionRanges.codeBasedSelections[0].range,
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
defaultPlanes: s.defaultPlanes,
|
||||
})
|
||||
)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
@ -140,15 +139,16 @@ export function useCalc({
|
||||
}, [ast, programMemory, selectionRange])
|
||||
|
||||
useEffect(() => {
|
||||
if (!engineCommandManager) return
|
||||
try {
|
||||
const code = `const __result__ = ${value}\nshow(__result__)`
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const _programMem: any = { root: {}, return: null }
|
||||
availableVarInfo.variables.forEach(({ key, value }) => {
|
||||
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
||||
})
|
||||
executor(ast, _programMem, engineCommandManager).then((programMemory) => {
|
||||
if (!defaultPlanes) return
|
||||
executor(ast, _programMem, engineCommandManager, defaultPlanes!).then(
|
||||
(programMemory) => {
|
||||
const resultDeclaration = ast.body.find(
|
||||
(a) =>
|
||||
a.type === 'VariableDeclaration' &&
|
||||
@ -160,7 +160,8 @@ export function useCalc({
|
||||
const result = programMemory?.root?.__result__?.value
|
||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||
init && setValueNode(init)
|
||||
})
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
setCalcResult('NAN')
|
||||
setValueNode(null)
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||
import { useStore } from '../useStore'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { EngineCommand } from '../lang/std/engineConnection'
|
||||
import { useState } from 'react'
|
||||
@ -7,6 +6,7 @@ import { ActionButton } from '../components/ActionButton'
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons'
|
||||
import { isReducedMotion } from 'lang/util'
|
||||
import { AstExplorer } from './AstExplorer'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
|
||||
type SketchModeCmd = Extract<
|
||||
Extract<EngineCommand, { type: 'modeling_cmd_req' }>['cmd'],
|
||||
@ -14,9 +14,6 @@ type SketchModeCmd = Extract<
|
||||
>
|
||||
|
||||
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
||||
const { engineCommandManager } = useStore((s) => ({
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
}))
|
||||
const [sketchModeCmd, setSketchModeCmd] = useState<SketchModeCmd>({
|
||||
type: 'default_camera_enable_sketch_mode',
|
||||
origin: { x: 0, y: 0, z: 0 },
|
||||
@ -70,19 +67,18 @@ export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
||||
className="w-16"
|
||||
type="checkbox"
|
||||
checked={sketchModeCmd.ortho}
|
||||
onChange={(a) => {
|
||||
console.log(a, (a as any).checked)
|
||||
onChange={(a) =>
|
||||
setSketchModeCmd({
|
||||
...sketchModeCmd,
|
||||
ortho: a.target.checked,
|
||||
})
|
||||
}}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() => {
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: sketchModeCmd,
|
||||
cmd_id: uuidv4(),
|
||||
|
@ -47,11 +47,9 @@ export const ErrorPage = () => {
|
||||
Clear storage
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
Element="link"
|
||||
Element="externalLink"
|
||||
icon={{ icon: faBug }}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
to="https://discord.com/channels/915388055236509727/1138967922614743060"
|
||||
to="https://github.com/KittyCAD/modeling-app/issues/new"
|
||||
>
|
||||
Report Bug
|
||||
</ActionButton>
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useStore } from '../useStore'
|
||||
import { faFileExport, faXmark } from '@fortawesome/free-solid-svg-icons'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import Modal from 'react-modal'
|
||||
import React from 'react'
|
||||
import { useFormik } from 'formik'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
|
||||
type OutputFormat = Models['OutputFormat_type']
|
||||
type OutputTypeKey = OutputFormat['type']
|
||||
type ExtractStorageTypes<T> = T extends { storage: infer U } ? U : never
|
||||
type StorageUnion = ExtractStorageTypes<OutputFormat>
|
||||
|
||||
interface ExportButtonProps extends React.PropsWithChildren {
|
||||
className?: {
|
||||
@ -18,14 +22,19 @@ interface ExportButtonProps extends React.PropsWithChildren {
|
||||
}
|
||||
|
||||
export const ExportButton = ({ children, className }: ExportButtonProps) => {
|
||||
const { engineCommandManager } = useStore((s) => ({
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
}))
|
||||
|
||||
const [modalIsOpen, setIsOpen] = React.useState(false)
|
||||
const {
|
||||
settings: {
|
||||
state: {
|
||||
context: { baseUnit },
|
||||
},
|
||||
},
|
||||
} = useGlobalStateContext()
|
||||
|
||||
const defaultType = 'gltf'
|
||||
const [type, setType] = React.useState(defaultType)
|
||||
const [type, setType] = React.useState<OutputTypeKey>(defaultType)
|
||||
const defaultStorage = 'embedded'
|
||||
const [storage, setStorage] = React.useState<StorageUnion>(defaultStorage)
|
||||
|
||||
function openModal() {
|
||||
setIsOpen(true)
|
||||
@ -38,7 +47,7 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
|
||||
// Default to gltf and embedded.
|
||||
const initialValues: OutputFormat = {
|
||||
type: defaultType,
|
||||
storage: 'embedded',
|
||||
storage: defaultStorage,
|
||||
presentation: 'pretty',
|
||||
}
|
||||
const formik = useFormik({
|
||||
@ -66,7 +75,18 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
|
||||
},
|
||||
}
|
||||
}
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
if (values.type === 'obj' || values.type === 'stl') {
|
||||
values.units = baseUnit
|
||||
}
|
||||
if (
|
||||
values.type === 'ply' ||
|
||||
values.type === 'stl' ||
|
||||
values.type === 'gltf'
|
||||
) {
|
||||
// Set the storage type.
|
||||
values.storage = storage
|
||||
}
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'export',
|
||||
@ -75,6 +95,7 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
|
||||
// in the scene to export. In that case, you'd pass the IDs thru here.
|
||||
entity_ids: [],
|
||||
format: values,
|
||||
source_unit: baseUnit,
|
||||
},
|
||||
cmd_id: uuidv4(),
|
||||
})
|
||||
@ -109,7 +130,17 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
|
||||
id="type"
|
||||
name="type"
|
||||
onChange={(e) => {
|
||||
setType(e.target.value)
|
||||
setType(e.target.value as OutputTypeKey)
|
||||
if (e.target.value === 'gltf') {
|
||||
// Set default to embedded.
|
||||
setStorage('embedded')
|
||||
} else if (e.target.value === 'ply') {
|
||||
// Set default to ascii.
|
||||
setStorage('ascii')
|
||||
} else if (e.target.value === 'stl') {
|
||||
// Set default to ascii.
|
||||
setStorage('ascii')
|
||||
}
|
||||
formik.handleChange(e)
|
||||
}}
|
||||
className="bg-chalkboard-20 dark:bg-chalkboard-90 w-full"
|
||||
@ -127,10 +158,10 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
|
||||
<select
|
||||
id="storage"
|
||||
name="storage"
|
||||
onChange={formik.handleChange}
|
||||
value={
|
||||
'storage' in formik.values ? formik.values.storage : ''
|
||||
}
|
||||
onChange={(e) => {
|
||||
setStorage(e.target.value as StorageUnion)
|
||||
formik.handleChange(e)
|
||||
}}
|
||||
className="bg-chalkboard-20 dark:bg-chalkboard-90 w-full"
|
||||
>
|
||||
{type === 'gltf' && (
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { processMemory } from './MemoryPanel'
|
||||
import { parser_wasm } from '../lang/abstractSyntaxTree'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import { initPromise } from '../lang/rust'
|
||||
import { initPromise, parse } from '../lang/wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
@ -15,18 +14,20 @@ describe('processMemory', () => {
|
||||
}
|
||||
const otherVar = myFn(5)
|
||||
|
||||
const theExtrude = startSketchAt([0, 0])
|
||||
const theExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-2.4, myVar], %)
|
||||
|> lineTo([-0.76, otherVar], %)
|
||||
|> extrude(4, %)
|
||||
|
||||
const theSketch = startSketchAt([0, 0])
|
||||
const theSketch = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-3.35, 0.17], %)
|
||||
|> lineTo([0.98, 5.16], %)
|
||||
|> lineTo([2.15, 4.32], %)
|
||||
// |> rx(90, %)
|
||||
show(theExtrude, theSketch)`
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast, {
|
||||
root: {},
|
||||
return: null,
|
||||
|
@ -2,7 +2,7 @@ import ReactJson from 'react-json-view'
|
||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||
import { useStore } from '../useStore'
|
||||
import { useMemo } from 'react'
|
||||
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/executor'
|
||||
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
|
||||
import { Themes } from '../lib/theme'
|
||||
|
||||
interface MemoryPanelProps extends CollapsiblePanelProps {
|
||||
|
@ -2,6 +2,8 @@ import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
||||
import { ProjectWithEntryPointMetadata } from '../Router'
|
||||
import { GlobalStateProvider } from './GlobalStateProvider'
|
||||
import CommandBarProvider from './CommandBar'
|
||||
|
||||
const now = new Date()
|
||||
const projectWellFormed = {
|
||||
@ -38,7 +40,11 @@ describe('ProjectSidebarMenu tests', () => {
|
||||
test('Renders the project name', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>
|
||||
<ProjectSidebarMenu project={projectWellFormed} />
|
||||
</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
@ -55,7 +61,11 @@ describe('ProjectSidebarMenu tests', () => {
|
||||
test('Renders app name if given no project', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>
|
||||
<ProjectSidebarMenu />
|
||||
</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
@ -69,7 +79,14 @@ describe('ProjectSidebarMenu tests', () => {
|
||||
test('Renders as a link if set to do so', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ProjectSidebarMenu project={projectWellFormed} renderAsLink={true} />
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>
|
||||
<ProjectSidebarMenu
|
||||
project={projectWellFormed}
|
||||
renderAsLink={true}
|
||||
/>
|
||||
</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { Value } from '../lang/abstractSyntaxTreeTypes'
|
||||
import { Value } from '../lang/wasm'
|
||||
import {
|
||||
AvailableVars,
|
||||
addToInputHelper,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { Value } from '../lang/abstractSyntaxTreeTypes'
|
||||
import { Value } from '../lang/wasm'
|
||||
import {
|
||||
AvailableVars,
|
||||
addToInputHelper,
|
||||
|
@ -20,11 +20,15 @@ import {
|
||||
compareVec2Epsilon,
|
||||
} from 'lang/std/sketch'
|
||||
import { getNodeFromPath } from 'lang/queryAst'
|
||||
import { Program, VariableDeclarator } from 'lang/abstractSyntaxTreeTypes'
|
||||
import { modify_ast_for_sketch } from '../wasm-lib/pkg/wasm_lib'
|
||||
import {
|
||||
Program,
|
||||
VariableDeclarator,
|
||||
rangeTypeFix,
|
||||
modifyAstForSketch,
|
||||
} from 'lang/wasm'
|
||||
import { KCLError } from 'lang/errors'
|
||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||
import { rangeTypeFix } from 'lang/abstractSyntaxTree'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
|
||||
export const Stream = ({ className = '' }) => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
@ -32,7 +36,6 @@ export const Stream = ({ className = '' }) => {
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const {
|
||||
mediaStream,
|
||||
engineCommandManager,
|
||||
setButtonDownInStream,
|
||||
didDragInStream,
|
||||
setDidDragInStream,
|
||||
@ -43,9 +46,10 @@ export const Stream = ({ className = '' }) => {
|
||||
updateAst,
|
||||
setGuiMode,
|
||||
programMemory,
|
||||
defaultPlanes,
|
||||
currentPlane,
|
||||
} = useStore((s) => ({
|
||||
mediaStream: s.mediaStream,
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
setButtonDownInStream: s.setButtonDownInStream,
|
||||
fileId: s.fileId,
|
||||
didDragInStream: s.didDragInStream,
|
||||
@ -57,6 +61,8 @@ export const Stream = ({ className = '' }) => {
|
||||
updateAst: s.updateAst,
|
||||
setGuiMode: s.setGuiMode,
|
||||
programMemory: s.programMemory,
|
||||
defaultPlanes: s.defaultPlanes,
|
||||
currentPlane: s.currentPlane,
|
||||
}))
|
||||
const {
|
||||
settings: {
|
||||
@ -73,7 +79,7 @@ export const Stream = ({ className = '' }) => {
|
||||
if (!videoRef.current) return
|
||||
if (!mediaStream) return
|
||||
videoRef.current.srcObject = mediaStream
|
||||
}, [mediaStream, engineCommandManager])
|
||||
}, [mediaStream])
|
||||
|
||||
const handleMouseDown: MouseEventHandler<HTMLVideoElement> = (e) => {
|
||||
if (!videoRef.current) return
|
||||
@ -107,7 +113,7 @@ export const Stream = ({ className = '' }) => {
|
||||
}
|
||||
|
||||
if (guiMode.mode === 'sketch' && guiMode.sketchMode === ('move' as any)) {
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'handle_mouse_drag_start',
|
||||
@ -121,7 +127,7 @@ export const Stream = ({ className = '' }) => {
|
||||
guiMode.sketchMode === ('sketch_line' as any)
|
||||
)
|
||||
) {
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'camera_drag_start',
|
||||
@ -139,7 +145,7 @@ export const Stream = ({ className = '' }) => {
|
||||
const handleScroll: WheelEventHandler<HTMLVideoElement> = (e) => {
|
||||
if (!cameraMouseDragGuards[cameraControls].zoom.scrollCallback(e)) return
|
||||
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'default_camera_zoom',
|
||||
@ -177,7 +183,7 @@ export const Stream = ({ className = '' }) => {
|
||||
}
|
||||
|
||||
if (!didDragInStream) {
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'select_with_point',
|
||||
@ -214,7 +220,7 @@ export const Stream = ({ className = '' }) => {
|
||||
window: { x, y },
|
||||
}
|
||||
}
|
||||
engineCommandManager?.sendSceneCommand(command).then(async (resp) => {
|
||||
engineCommandManager.sendSceneCommand(command).then(async (resp) => {
|
||||
if (!(guiMode.mode === 'sketch')) return
|
||||
|
||||
if (guiMode.sketchMode === 'selectFace') return
|
||||
@ -240,9 +246,6 @@ export const Stream = ({ className = '' }) => {
|
||||
) {
|
||||
// Let's get the updated ast.
|
||||
if (sketchGroupId === '') return
|
||||
|
||||
console.log('guiMode.pathId', guiMode.pathId)
|
||||
|
||||
// We have a problem if we do not have an id for the sketch group.
|
||||
if (
|
||||
guiMode.pathId === undefined ||
|
||||
@ -253,26 +256,30 @@ export const Stream = ({ className = '' }) => {
|
||||
|
||||
let engineId = guiMode.pathId
|
||||
|
||||
try {
|
||||
const updatedAst: Program = await modify_ast_for_sketch(
|
||||
// Get the current plane string for plane we are on.
|
||||
let currentPlaneString = ''
|
||||
if (currentPlane === defaultPlanes?.xy) {
|
||||
currentPlaneString = 'XY'
|
||||
} else if (currentPlane === defaultPlanes?.yz) {
|
||||
currentPlaneString = 'YZ'
|
||||
} else if (currentPlane === defaultPlanes?.xz) {
|
||||
currentPlaneString = 'XZ'
|
||||
}
|
||||
|
||||
// Do not supporting editing/moving lines on a non-default plane.
|
||||
// Eventually we can support this but for now we will just throw an
|
||||
// error.
|
||||
if (currentPlaneString === '') return
|
||||
|
||||
const updatedAst: Program = await modifyAstForSketch(
|
||||
engineCommandManager,
|
||||
JSON.stringify(ast),
|
||||
ast,
|
||||
variableName,
|
||||
currentPlaneString,
|
||||
engineId
|
||||
)
|
||||
|
||||
updateAst(updatedAst, false)
|
||||
} catch (e: any) {
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
const kclError = new KCLError(
|
||||
parsed.kind,
|
||||
parsed.msg,
|
||||
rangeTypeFix(parsed.sourceRanges)
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
throw kclError
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -285,7 +292,7 @@ export const Stream = ({ className = '' }) => {
|
||||
guiMode.waitingFirstClick &&
|
||||
!isEditingExistingSketch
|
||||
) {
|
||||
const curve = await engineCommandManager?.sendSceneCommand({
|
||||
const curve = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
@ -295,8 +302,46 @@ export const Stream = ({ className = '' }) => {
|
||||
})
|
||||
const coords: { x: number; y: number }[] =
|
||||
curve.data.data.control_points
|
||||
|
||||
// We need the normal for the plane we are on.
|
||||
const plane = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'get_sketch_mode_plane',
|
||||
},
|
||||
})
|
||||
const z_axis = plane.data.data.z_axis
|
||||
|
||||
// Get the current axis.
|
||||
let currentAxis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz' | null =
|
||||
null
|
||||
if (currentPlane === defaultPlanes?.xy) {
|
||||
if (z_axis.z === -1) {
|
||||
currentAxis = '-xy'
|
||||
} else {
|
||||
currentAxis = 'xy'
|
||||
}
|
||||
} else if (currentPlane === defaultPlanes?.yz) {
|
||||
if (z_axis.x === -1) {
|
||||
currentAxis = '-yz'
|
||||
} else {
|
||||
currentAxis = 'yz'
|
||||
}
|
||||
} else if (currentPlane === defaultPlanes?.xz) {
|
||||
if (z_axis.y === -1) {
|
||||
currentAxis = '-xz'
|
||||
} else {
|
||||
currentAxis = 'xz'
|
||||
}
|
||||
}
|
||||
|
||||
// Do not support starting a new sketch on a non-default plane.
|
||||
if (!currentAxis) return
|
||||
|
||||
const _addStartSketch = addStartSketch(
|
||||
ast,
|
||||
currentAxis,
|
||||
[roundOff(coords[0].x), roundOff(coords[0].y)],
|
||||
[
|
||||
roundOff(coords[1].x - coords[0].x),
|
||||
@ -326,7 +371,7 @@ export const Stream = ({ className = '' }) => {
|
||||
resp?.data?.data?.entities_modified?.length &&
|
||||
(!guiMode.waitingFirstClick || isEditingExistingSketch)
|
||||
) {
|
||||
const curve = await engineCommandManager?.sendSceneCommand({
|
||||
const curve = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
@ -371,12 +416,12 @@ export const Stream = ({ className = '' }) => {
|
||||
setGuiMode({
|
||||
mode: 'default',
|
||||
})
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'edit_mode_exit' },
|
||||
})
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'default_camera_disable_sketch_mode' },
|
||||
@ -415,6 +460,7 @@ export const Stream = ({ className = '' }) => {
|
||||
onWheel={handleScroll}
|
||||
onPlay={() => setIsLoading(false)}
|
||||
onMouseMoveCapture={handleMouseMove}
|
||||
disablePictureInPicture
|
||||
className={`w-full h-full ${isExecuting && 'blur-md'}`}
|
||||
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
|
||||
/>
|
||||
|
@ -30,6 +30,7 @@ import { isOverlap, roundOff } from 'lib/utils'
|
||||
import { kclErrToDiagnostic } from 'lang/errors'
|
||||
import { CSSRuleObject } from 'tailwindcss/types/config'
|
||||
import interact from '@replit/codemirror-interact'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
|
||||
export const editorShortcutMeta = {
|
||||
formatCode: {
|
||||
@ -52,7 +53,6 @@ export const TextEditor = ({
|
||||
code,
|
||||
deferredSetCode,
|
||||
editorView,
|
||||
engineCommandManager,
|
||||
formatCode,
|
||||
isLSPServerReady,
|
||||
selectionRanges,
|
||||
@ -64,7 +64,6 @@ export const TextEditor = ({
|
||||
code: s.code,
|
||||
deferredSetCode: s.deferredSetCode,
|
||||
editorView: s.editorView,
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
formatCode: s.formatCode,
|
||||
isLSPServerReady: s.isLSPServerReady,
|
||||
selectionRanges: s.selectionRanges,
|
||||
@ -173,7 +172,7 @@ export const TextEditor = ({
|
||||
const idBasedSelections = codeBasedSelections
|
||||
.map(({ type, range }) => {
|
||||
const hasOverlap = Object.entries(
|
||||
engineCommandManager?.sourceRangeMap || {}
|
||||
engineCommandManager.sourceRangeMap || {}
|
||||
).filter(([_, sourceRange]) => {
|
||||
return isOverlap(sourceRange, range)
|
||||
})
|
||||
@ -186,7 +185,7 @@ export const TextEditor = ({
|
||||
})
|
||||
.filter(Boolean) as any
|
||||
|
||||
engineCommandManager?.cusorsSelected({
|
||||
engineCommandManager.cusorsSelected({
|
||||
otherSelections: [],
|
||||
idBasedSelections,
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import { Value, VariableDeclarator } from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import { Value, VariableDeclarator } from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import { Value } from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
|
@ -1,11 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { create } from 'react-modal-promise'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import {
|
||||
BinaryPart,
|
||||
Value,
|
||||
VariableDeclarator,
|
||||
} from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import { Value } from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { create } from 'react-modal-promise'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import { Value } from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -133,7 +133,7 @@ export const SetAbsDistance = ({ buttonType }: { buttonType: ButtonType }) => {
|
||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||
})
|
||||
} catch (e) {
|
||||
console.log('e', e)
|
||||
console.log('error', e)
|
||||
}
|
||||
}}
|
||||
disabled={!enableAngLen}
|
||||
|
@ -1,11 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { create } from 'react-modal-promise'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import {
|
||||
BinaryPart,
|
||||
Value,
|
||||
VariableDeclarator,
|
||||
} from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
|
@ -1,11 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { create } from 'react-modal-promise'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import {
|
||||
BinaryPart,
|
||||
Value,
|
||||
VariableDeclarator,
|
||||
} from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -21,8 +17,6 @@ import { GetInfoModal } from '../SetHorVertDistanceModal'
|
||||
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { updateCursors } from '../../lang/util'
|
||||
import { ActionIcon } from 'components/ActionIcon'
|
||||
import { sketchButtonClassnames } from 'Toolbar'
|
||||
|
||||
const getModalInfo = create(GetInfoModal as any)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { create } from 'react-modal-promise'
|
||||
import { toolTips, useStore } from '../../useStore'
|
||||
import { Value } from '../../lang/abstractSyntaxTreeTypes'
|
||||
import { Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -147,7 +147,7 @@ export const SetAngleLength = ({
|
||||
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||||
})
|
||||
} catch (e) {
|
||||
console.log('e', e)
|
||||
console.log('erorr', e)
|
||||
}
|
||||
}}
|
||||
disabled={!enableAngLen}
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import UserSidebarMenu from './UserSidebarMenu'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import {
|
||||
Route,
|
||||
RouterProvider,
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
} from 'react-router-dom'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { GlobalStateProvider } from './GlobalStateProvider'
|
||||
import CommandBarProvider from './CommandBar'
|
||||
@ -93,11 +98,24 @@ describe('UserSidebarMenu tests', () => {
|
||||
|
||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||
// wrap in router and xState context
|
||||
return (
|
||||
<BrowserRouter>
|
||||
// We have to use a memory router in the testing environment,
|
||||
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
|
||||
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
|
||||
const router = createMemoryRouter(
|
||||
createRoutesFromElements(
|
||||
<Route
|
||||
path="/file/:id"
|
||||
element={
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>{children}</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
{
|
||||
initialEntries: ['/file/new'],
|
||||
initialIndex: 0,
|
||||
}
|
||||
)
|
||||
return <RouterProvider router={router} />
|
||||
}
|
||||
|
@ -1,18 +1,24 @@
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import { faBars, faGear, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faBars,
|
||||
faBug,
|
||||
faGear,
|
||||
faSignOutAlt,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { faGithub } from '@fortawesome/free-brands-svg-icons'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { paths } from '../Router'
|
||||
import makeUrlPathRelative from '../lib/makeUrlPathRelative'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
|
||||
type User = Models['User_type']
|
||||
|
||||
const UserSidebarMenu = ({ user }: { user?: User }) => {
|
||||
const location = useLocation()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const displayedName = getDisplayName(user)
|
||||
const [imageLoadFailed, setImageLoadFailed] = useState(false)
|
||||
const navigate = useNavigate()
|
||||
@ -127,23 +133,30 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
||||
// since /settings is a nested route the sidebar doesn't close
|
||||
// automatically when navigating to it
|
||||
close()
|
||||
navigate(
|
||||
(location.pathname.endsWith('/')
|
||||
? location.pathname.slice(0, -1)
|
||||
: location.pathname) + paths.SETTINGS
|
||||
)
|
||||
const targetPath = location.pathname.includes(paths.FILE)
|
||||
? filePath + paths.SETTINGS
|
||||
: paths.HOME + paths.SETTINGS
|
||||
navigate(targetPath)
|
||||
}}
|
||||
>
|
||||
Settings
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
Element="link"
|
||||
Element="externalLink"
|
||||
to="https://github.com/KittyCAD/modeling-app/discussions"
|
||||
icon={{ icon: faGithub }}
|
||||
className="border-transparent dark:border-transparent dark:hover:border-liquid-60"
|
||||
>
|
||||
Request a feature
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
Element="externalLink"
|
||||
to="https://github.com/KittyCAD/modeling-app/issues/new"
|
||||
icon={{ icon: faBug }}
|
||||
className="border-transparent dark:border-transparent dark:hover:border-liquid-60"
|
||||
>
|
||||
Report a bug
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() => send('Log out')}
|
||||
|
@ -109,7 +109,6 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
||||
}
|
||||
}
|
||||
messageString += message
|
||||
// console.log(messageString)
|
||||
return
|
||||
})
|
||||
|
||||
|
@ -96,8 +96,6 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
async sendChange({ documentText }: { documentText: string }) {
|
||||
if (!this.client.ready) return
|
||||
|
||||
console.log(documentText.length)
|
||||
|
||||
if (documentText.length > 5000) {
|
||||
// Clear out the text it thinks we have, large documents will throw a stack error.
|
||||
// This is obviously not a good fix but it works for now til we figure
|
||||
|
12
src/hooks/useAbsoluteFilePath.ts
Normal file
12
src/hooks/useAbsoluteFilePath.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { BROWSER_FILE_NAME, IndexLoaderData, paths } from 'Router'
|
||||
import { useRouteLoaderData } from 'react-router-dom'
|
||||
|
||||
export function useAbsoluteFilePath() {
|
||||
const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
|
||||
return (
|
||||
paths.FILE +
|
||||
'/' +
|
||||
encodeURIComponent(routeData?.project?.path || BROWSER_FILE_NAME)
|
||||
)
|
||||
}
|
@ -2,35 +2,37 @@
|
||||
// Once we have xState this should be removed
|
||||
|
||||
import { useStore, Selections } from 'useStore'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { ArtifactMap, EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { Models } from '@kittycad/lib/dist/types/src'
|
||||
import { isReducedMotion } from 'lang/util'
|
||||
import { isOverlap } from 'lib/utils'
|
||||
|
||||
interface DefaultPlanes {
|
||||
xy: string
|
||||
// TODO re-enable
|
||||
// yz: string
|
||||
// xz: string
|
||||
}
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
import { DefaultPlanes } from '../wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
import { getNodeFromPath } from '../lang/queryAst'
|
||||
import { CallExpression, PipeExpression } from '../lang/wasm'
|
||||
|
||||
export function useAppMode() {
|
||||
const {
|
||||
guiMode,
|
||||
setGuiMode,
|
||||
selectionRanges,
|
||||
engineCommandManager,
|
||||
selectionRangeTypeMap,
|
||||
defaultPlanes,
|
||||
setDefaultPlanes,
|
||||
setCurrentPlane,
|
||||
ast,
|
||||
} = useStore((s) => ({
|
||||
guiMode: s.guiMode,
|
||||
setGuiMode: s.setGuiMode,
|
||||
selectionRanges: s.selectionRanges,
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
selectionRangeTypeMap: s.selectionRangeTypeMap,
|
||||
defaultPlanes: s.defaultPlanes,
|
||||
setDefaultPlanes: s.setDefaultPlanes,
|
||||
setCurrentPlane: s.setCurrentPlane,
|
||||
ast: s.ast,
|
||||
}))
|
||||
const [defaultPlanes, setDefaultPlanes] = useState<DefaultPlanes | null>(null)
|
||||
useEffect(() => {
|
||||
if (
|
||||
guiMode.mode === 'sketch' &&
|
||||
@ -40,8 +42,10 @@ export function useAppMode() {
|
||||
const createAndShowPlanes = async () => {
|
||||
let localDefaultPlanes: DefaultPlanes
|
||||
if (!defaultPlanes) {
|
||||
localDefaultPlanes = await initDefaultPlanes(engineCommandManager)
|
||||
setDefaultPlanes(localDefaultPlanes)
|
||||
const newDefaultPlanes = await initDefaultPlanes(engineCommandManager)
|
||||
if (!newDefaultPlanes) return
|
||||
setDefaultPlanes(newDefaultPlanes)
|
||||
localDefaultPlanes = newDefaultPlanes
|
||||
} else {
|
||||
localDefaultPlanes = defaultPlanes
|
||||
}
|
||||
@ -57,20 +61,52 @@ export function useAppMode() {
|
||||
const enableSketchMode = async () => {
|
||||
let localDefaultPlanes: DefaultPlanes
|
||||
if (!defaultPlanes) {
|
||||
localDefaultPlanes = await initDefaultPlanes(engineCommandManager)
|
||||
setDefaultPlanes(localDefaultPlanes)
|
||||
const newDefaultPlanes = await initDefaultPlanes(engineCommandManager)
|
||||
if (!newDefaultPlanes) return
|
||||
setDefaultPlanes(newDefaultPlanes)
|
||||
localDefaultPlanes = newDefaultPlanes
|
||||
} else {
|
||||
localDefaultPlanes = defaultPlanes
|
||||
}
|
||||
setDefaultPlanesHidden(engineCommandManager, localDefaultPlanes, true)
|
||||
// TODO figure out the plane to use based on the sketch
|
||||
// maybe it's easier to make a new plane than rely on the defaults
|
||||
await engineCommandManager?.sendSceneCommand({
|
||||
|
||||
const pipeExpression = getNodeFromPath<PipeExpression>(
|
||||
ast,
|
||||
guiMode.pathToNode,
|
||||
'PipeExpression'
|
||||
).node
|
||||
if (pipeExpression.type !== 'PipeExpression') return /// bad bad bad
|
||||
const sketchCallExpression = pipeExpression.body.find(
|
||||
(e) =>
|
||||
e.type === 'CallExpression' && e.callee.name === 'startSketchOn'
|
||||
) as CallExpression
|
||||
if (!sketchCallExpression) return // also bad bad bad
|
||||
const firstArg = sketchCallExpression.arguments[0]
|
||||
let planeId = ''
|
||||
if (firstArg.type === 'Literal' && firstArg.value) {
|
||||
const planeStrCleaned = firstArg.value
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.replace('-', '')
|
||||
if (
|
||||
planeStrCleaned === 'xy' ||
|
||||
planeStrCleaned === 'xz' ||
|
||||
planeStrCleaned === 'yz'
|
||||
) {
|
||||
planeId = localDefaultPlanes[planeStrCleaned]
|
||||
}
|
||||
}
|
||||
|
||||
if (!planeId) return // they are on some non default plane, which we don't support yet
|
||||
|
||||
setCurrentPlane(planeId)
|
||||
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'sketch_mode_enable',
|
||||
plane_id: localDefaultPlanes.xy,
|
||||
plane_id: planeId,
|
||||
ortho: true,
|
||||
animated: !isReducedMotion(),
|
||||
},
|
||||
@ -135,7 +171,7 @@ export function useAppMode() {
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
const unSub = engineCommandManager?.subscribeTo({
|
||||
const unSub = engineCommandManager.subscribeTo({
|
||||
event: 'select_with_point',
|
||||
callback: async ({ data }) => {
|
||||
if (!data.entity_id) return
|
||||
@ -144,8 +180,8 @@ export function useAppMode() {
|
||||
// user clicked something else in the scene
|
||||
return
|
||||
}
|
||||
const sketchModeResponse = await engineCommandManager?.sendSceneCommand(
|
||||
{
|
||||
setCurrentPlane(data.entity_id)
|
||||
const sketchModeResponse = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
@ -154,8 +190,7 @@ export function useAppMode() {
|
||||
ortho: true,
|
||||
animated: !isReducedMotion(),
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
setDefaultPlanesHidden(engineCommandManager, defaultPlanes, true)
|
||||
const sketchUuid = uuidv4()
|
||||
const proms: any[] = []
|
||||
@ -178,8 +213,7 @@ export function useAppMode() {
|
||||
},
|
||||
})
|
||||
)
|
||||
const res = await Promise.all(proms)
|
||||
console.log('res', res)
|
||||
await Promise.all(proms)
|
||||
setGuiMode({
|
||||
mode: 'sketch',
|
||||
sketchMode: 'sketchEdit',
|
||||
@ -202,14 +236,16 @@ async function createPlane(
|
||||
x_axis,
|
||||
y_axis,
|
||||
color,
|
||||
hidden,
|
||||
}: {
|
||||
x_axis: Models['Point3d_type']
|
||||
y_axis: Models['Point3d_type']
|
||||
color: Models['Color_type']
|
||||
hidden: boolean
|
||||
}
|
||||
) {
|
||||
const planeId = uuidv4()
|
||||
await engineCommandManager?.sendSceneCommand({
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'make_plane',
|
||||
@ -218,10 +254,11 @@ async function createPlane(
|
||||
x_axis,
|
||||
y_axis,
|
||||
clobber: false,
|
||||
hide: hidden,
|
||||
},
|
||||
cmd_id: planeId,
|
||||
})
|
||||
await engineCommandManager?.sendSceneCommand({
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'plane_set_color',
|
||||
@ -233,13 +270,22 @@ async function createPlane(
|
||||
return planeId
|
||||
}
|
||||
|
||||
function setDefaultPlanesHidden(
|
||||
engineCommandManager: EngineCommandManager | undefined,
|
||||
export function setDefaultPlanesHidden(
|
||||
engineCommandManager: EngineCommandManager,
|
||||
defaultPlanes: DefaultPlanes,
|
||||
hidden: boolean
|
||||
) {
|
||||
Object.values(defaultPlanes).forEach((planeId) => {
|
||||
engineCommandManager?.sendSceneCommand({
|
||||
hidePlane(engineCommandManager, planeId, hidden)
|
||||
})
|
||||
}
|
||||
|
||||
function hidePlane(
|
||||
engineCommandManager: EngineCommandManager,
|
||||
planeId: string,
|
||||
hidden: boolean
|
||||
) {
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
@ -248,37 +294,49 @@ function setDefaultPlanesHidden(
|
||||
hidden: hidden,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function initDefaultPlanes(
|
||||
engineCommandManager: EngineCommandManager
|
||||
): Promise<DefaultPlanes> {
|
||||
export async function initDefaultPlanes(
|
||||
engineCommandManager: EngineCommandManager,
|
||||
hidePlanes?: boolean
|
||||
): Promise<DefaultPlanes | null> {
|
||||
if (!engineCommandManager.engineConnection?.isReady()) {
|
||||
return null
|
||||
}
|
||||
const xy = await createPlane(engineCommandManager, {
|
||||
x_axis: { x: 1, y: 0, z: 0 },
|
||||
y_axis: { x: 0, y: 1, z: 0 },
|
||||
color: { r: 0.7, g: 0.28, b: 0.28, a: 0.4 },
|
||||
hidden: hidePlanes ? true : false,
|
||||
})
|
||||
// TODO re-enable
|
||||
// const yz = createPlane(engineCommandManager, {
|
||||
// x_axis: { x: 0, y: 1, z: 0 },
|
||||
// y_axis: { x: 0, y: 0, z: 1 },
|
||||
// color: { r: 0.28, g: 0.7, b: 0.28, a: 0.4 },
|
||||
// })
|
||||
// const xz = createPlane(engineCommandManager, {
|
||||
// x_axis: { x: 1, y: 0, z: 0 },
|
||||
// y_axis: { x: 0, y: 0, z: 1 },
|
||||
// color: { r: 0.28, g: 0.28, b: 0.7, a: 0.4 },
|
||||
// })
|
||||
return { xy }
|
||||
if (hidePlanes) {
|
||||
hidePlane(engineCommandManager, xy, true)
|
||||
}
|
||||
const yz = await createPlane(engineCommandManager, {
|
||||
x_axis: { x: 0, y: 1, z: 0 },
|
||||
y_axis: { x: 0, y: 0, z: 1 },
|
||||
color: { r: 0.28, g: 0.7, b: 0.28, a: 0.4 },
|
||||
hidden: hidePlanes ? true : false,
|
||||
})
|
||||
if (hidePlanes) {
|
||||
hidePlane(engineCommandManager, yz, true)
|
||||
}
|
||||
const xz = await createPlane(engineCommandManager, {
|
||||
x_axis: { x: 1, y: 0, z: 0 },
|
||||
y_axis: { x: 0, y: 0, z: 1 },
|
||||
color: { r: 0.28, g: 0.28, b: 0.7, a: 0.4 },
|
||||
hidden: hidePlanes ? true : false,
|
||||
})
|
||||
return { xy, yz, xz }
|
||||
}
|
||||
|
||||
function isCursorInSketchCommandRange(
|
||||
artifactMap: ArtifactMap,
|
||||
selectionRanges: Selections
|
||||
): string | false {
|
||||
const overlapingEntries = Object.entries(artifactMap || {}).filter(
|
||||
([id, artifact]) =>
|
||||
const overlapingEntries: [string, ArtifactMap[string]][] = Object.entries(
|
||||
artifactMap
|
||||
).filter(([id, artifact]: [string, ArtifactMap[string]]) =>
|
||||
selectionRanges.codeBasedSelections.some(
|
||||
(selection) =>
|
||||
Array.isArray(selection?.range) &&
|
||||
|
@ -1,14 +1,9 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useStore } from 'useStore'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
|
||||
export function useEngineConnectionSubscriptions() {
|
||||
const {
|
||||
engineCommandManager,
|
||||
setCursor2,
|
||||
setHighlightRange,
|
||||
highlightRange,
|
||||
} = useStore((s) => ({
|
||||
engineCommandManager: s.engineCommandManager,
|
||||
const { setCursor2, setHighlightRange, highlightRange } = useStore((s) => ({
|
||||
setCursor2: s.setCursor2,
|
||||
setHighlightRange: s.setHighlightRange,
|
||||
highlightRange: s.highlightRange,
|
||||
|
@ -1,53 +1,92 @@
|
||||
import { useLayoutEffect } from 'react'
|
||||
import { _executor } from '../lang/executor'
|
||||
import { useLayoutEffect, useEffect, useRef } from 'react'
|
||||
import { _executor } from '../lang/wasm'
|
||||
import { useStore } from '../useStore'
|
||||
import { EngineCommandManager } from '../lang/std/engineConnection'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
import { deferExecution } from 'lib/utils'
|
||||
|
||||
export function useSetupEngineManager(
|
||||
streamRef: React.RefObject<HTMLDivElement>,
|
||||
token?: string
|
||||
) {
|
||||
const {
|
||||
setEngineCommandManager,
|
||||
setMediaStream,
|
||||
setIsStreamReady,
|
||||
setStreamDimensions,
|
||||
streamDimensions,
|
||||
executeCode,
|
||||
} = useStore((s) => ({
|
||||
setEngineCommandManager: s.setEngineCommandManager,
|
||||
setMediaStream: s.setMediaStream,
|
||||
setIsStreamReady: s.setIsStreamReady,
|
||||
setStreamDimensions: s.setStreamDimensions,
|
||||
streamDimensions: s.streamDimensions,
|
||||
executeCode: s.executeCode,
|
||||
}))
|
||||
|
||||
const streamWidth = streamRef?.current?.offsetWidth
|
||||
const streamHeight = streamRef?.current?.offsetHeight
|
||||
|
||||
const width = streamWidth ? streamWidth : 0
|
||||
const quadWidth = Math.round(width / 4) * 4
|
||||
const height = streamHeight ? streamHeight : 0
|
||||
const quadHeight = Math.round(height / 4) * 4
|
||||
const hasSetNonZeroDimensions = useRef<boolean>(false)
|
||||
|
||||
useEffect(() => {
|
||||
executeCode()
|
||||
}, [])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setStreamDimensions({
|
||||
streamWidth: quadWidth,
|
||||
streamHeight: quadHeight,
|
||||
})
|
||||
if (!width || !height) return
|
||||
const eng = new EngineCommandManager({
|
||||
// Load the engine command manager once with the initial width and height,
|
||||
// then we do not want to reload it.
|
||||
const { width: quadWidth, height: quadHeight } = getDimensions(
|
||||
streamWidth,
|
||||
streamHeight
|
||||
)
|
||||
if (!hasSetNonZeroDimensions.current && quadHeight && quadWidth) {
|
||||
engineCommandManager.start({
|
||||
setMediaStream,
|
||||
setIsStreamReady,
|
||||
width: quadWidth,
|
||||
height: quadHeight,
|
||||
executeCode,
|
||||
token,
|
||||
})
|
||||
setEngineCommandManager(eng)
|
||||
eng.waitForReady.then(() => {
|
||||
executeCode()
|
||||
setStreamDimensions({
|
||||
streamWidth: quadWidth,
|
||||
streamHeight: quadHeight,
|
||||
})
|
||||
hasSetNonZeroDimensions.current = true
|
||||
}
|
||||
}, [streamRef?.current?.offsetWidth, streamRef?.current?.offsetHeight])
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = deferExecution(() => {
|
||||
const { width, height } = getDimensions(
|
||||
streamRef?.current?.offsetWidth,
|
||||
streamRef?.current?.offsetHeight
|
||||
)
|
||||
if (
|
||||
streamDimensions.streamWidth !== width ||
|
||||
streamDimensions.streamHeight !== height
|
||||
) {
|
||||
engineCommandManager.handleResize({
|
||||
streamWidth: width,
|
||||
streamHeight: height,
|
||||
})
|
||||
setStreamDimensions({
|
||||
streamWidth: width,
|
||||
streamHeight: height,
|
||||
})
|
||||
}
|
||||
}, 500)
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
return () => {
|
||||
eng?.tearDown()
|
||||
window.removeEventListener('resize', handleResize)
|
||||
}
|
||||
}, [quadWidth, quadHeight])
|
||||
}, [])
|
||||
}
|
||||
|
||||
function getDimensions(streamWidth?: number, streamHeight?: number) {
|
||||
const width = streamWidth ? streamWidth : 0
|
||||
const quadWidth = Math.round(width / 4) * 4
|
||||
const height = streamHeight ? streamHeight : 0
|
||||
const quadHeight = Math.round(height / 4) * 4
|
||||
return { width: quadWidth, height: quadHeight }
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ export function useConvertToVariable() {
|
||||
|
||||
updateAst(_modifiedAst, true)
|
||||
} catch (e) {
|
||||
console.log('e', e)
|
||||
console.log('error', e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { parser_wasm } from './abstractSyntaxTree'
|
||||
import { KCLError } from './errors'
|
||||
import { initPromise } from './rust'
|
||||
import { initPromise, parse } from './wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
describe('testing AST', () => {
|
||||
test('5 + 6', () => {
|
||||
const result = parser_wasm('5 +6')
|
||||
const result = parse('5 +6')
|
||||
delete (result as any).nonCodeMeta
|
||||
expect(result.body).toEqual([
|
||||
{
|
||||
@ -37,7 +36,7 @@ describe('testing AST', () => {
|
||||
])
|
||||
})
|
||||
test('const myVar = 5', () => {
|
||||
const { body } = parser_wasm('const myVar = 5')
|
||||
const { body } = parse('const myVar = 5')
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -71,7 +70,7 @@ describe('testing AST', () => {
|
||||
const code = `const myVar = 5
|
||||
const newVar = myVar + 1
|
||||
`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -143,7 +142,7 @@ const newVar = myVar + 1
|
||||
|
||||
describe('testing function declaration', () => {
|
||||
test('fn funcN = () => {}', () => {
|
||||
const { body } = parser_wasm('fn funcN = () => {}')
|
||||
const { body } = parse('fn funcN = () => {}')
|
||||
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
||||
expect(body).toEqual([
|
||||
{
|
||||
@ -179,7 +178,7 @@ describe('testing function declaration', () => {
|
||||
])
|
||||
})
|
||||
test('fn funcN = (a, b) => {return a + b}', () => {
|
||||
const { body } = parser_wasm(
|
||||
const { body } = parse(
|
||||
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
|
||||
)
|
||||
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
||||
@ -256,7 +255,7 @@ describe('testing function declaration', () => {
|
||||
test('call expression assignment', () => {
|
||||
const code = `fn funcN = (a, b) => { return a + b }
|
||||
const myVar = funcN(1, 2)`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
||||
expect(body).toEqual([
|
||||
{
|
||||
@ -388,7 +387,7 @@ describe('testing pipe operator special', () => {
|
||||
|> lineTo([1, 1], %)
|
||||
|> rx(45, %)
|
||||
`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
delete (body[0] as any).declarations[0].init.nonCodeMeta
|
||||
expect(body).toEqual([
|
||||
{
|
||||
@ -404,7 +403,7 @@ describe('testing pipe operator special', () => {
|
||||
id: { type: 'Identifier', start: 6, end: 14, name: 'mySketch' },
|
||||
init: {
|
||||
type: 'PipeExpression',
|
||||
start: 15,
|
||||
start: 17,
|
||||
end: 145,
|
||||
body: [
|
||||
{
|
||||
@ -624,7 +623,7 @@ describe('testing pipe operator special', () => {
|
||||
})
|
||||
test('pipe operator with binary expression', () => {
|
||||
let code = `const myVar = 5 + 6 |> myFunc(45, %)`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
delete (body as any)[0].declarations[0].init.nonCodeMeta
|
||||
expect(body).toEqual([
|
||||
{
|
||||
@ -645,7 +644,7 @@ describe('testing pipe operator special', () => {
|
||||
},
|
||||
init: {
|
||||
type: 'PipeExpression',
|
||||
start: 12,
|
||||
start: 14,
|
||||
end: 36,
|
||||
body: [
|
||||
{
|
||||
@ -706,7 +705,7 @@ describe('testing pipe operator special', () => {
|
||||
})
|
||||
test('array expression', () => {
|
||||
let code = `const yo = [1, '2', three, 4 + 5]`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -781,7 +780,7 @@ describe('testing pipe operator special', () => {
|
||||
'const three = 3',
|
||||
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
||||
].join('\n')
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -925,7 +924,7 @@ describe('testing pipe operator special', () => {
|
||||
const code = `const yo = {key: {
|
||||
key2: 'value'
|
||||
}}`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -993,7 +992,7 @@ describe('testing pipe operator special', () => {
|
||||
})
|
||||
test('object expression with array ast', () => {
|
||||
const code = `const yo = {key: [1, '2']}`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -1057,7 +1056,7 @@ describe('testing pipe operator special', () => {
|
||||
})
|
||||
test('object memberExpression simple', () => {
|
||||
const code = `const prop = yo.one.two`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -1112,7 +1111,7 @@ describe('testing pipe operator special', () => {
|
||||
})
|
||||
test('object memberExpression with square braces', () => {
|
||||
const code = `const prop = yo.one["two"]`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -1168,7 +1167,7 @@ describe('testing pipe operator special', () => {
|
||||
})
|
||||
test('object memberExpression with two square braces literal and identifier', () => {
|
||||
const code = `const prop = yo["one"][two]`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body).toEqual([
|
||||
{
|
||||
type: 'VariableDeclaration',
|
||||
@ -1227,7 +1226,7 @@ describe('testing pipe operator special', () => {
|
||||
describe('nests binary expressions correctly', () => {
|
||||
it('works with the simple case', () => {
|
||||
const code = `const yo = 1 + 2`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body[0]).toEqual({
|
||||
type: 'VariableDeclaration',
|
||||
start: 0,
|
||||
@ -1271,7 +1270,7 @@ describe('nests binary expressions correctly', () => {
|
||||
it('should nest according to precedence with multiply first', () => {
|
||||
// should be binExp { binExp { lit-1 * lit-2 } + lit}
|
||||
const code = `const yo = 1 * 2 + 3`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body[0]).toEqual({
|
||||
type: 'VariableDeclaration',
|
||||
start: 0,
|
||||
@ -1328,7 +1327,7 @@ describe('nests binary expressions correctly', () => {
|
||||
it('should nest according to precedence with sum first', () => {
|
||||
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
|
||||
const code = `const yo = 1 + 2 * 3`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect(body[0]).toEqual({
|
||||
type: 'VariableDeclaration',
|
||||
start: 0,
|
||||
@ -1384,7 +1383,7 @@ describe('nests binary expressions correctly', () => {
|
||||
})
|
||||
it('should nest properly with two opperators of equal precedence', () => {
|
||||
const code = `const yo = 1 + 2 - 3`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect((body[0] as any).declarations[0].init).toEqual({
|
||||
type: 'BinaryExpression',
|
||||
start: 11,
|
||||
@ -1421,7 +1420,7 @@ describe('nests binary expressions correctly', () => {
|
||||
})
|
||||
it('should nest properly with two opperators of equal (but higher) precedence', () => {
|
||||
const code = `const yo = 1 * 2 / 3`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
expect((body[0] as any).declarations[0].init).toEqual({
|
||||
type: 'BinaryExpression',
|
||||
start: 11,
|
||||
@ -1458,7 +1457,7 @@ describe('nests binary expressions correctly', () => {
|
||||
})
|
||||
it('should nest properly with longer example', () => {
|
||||
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
const init = (body[0] as any).declarations[0].init
|
||||
expect(init).toEqual({
|
||||
type: 'BinaryExpression',
|
||||
@ -1520,14 +1519,12 @@ const key = 'c'`
|
||||
value: 'this is a comment',
|
||||
},
|
||||
}
|
||||
const { nonCodeMeta } = parser_wasm(code)
|
||||
const { nonCodeMeta } = parse(code)
|
||||
expect(nonCodeMeta.nonCodeNodes[0]).toEqual(nonCodeMetaInstance)
|
||||
|
||||
// extra whitespace won't change it's position (0) or value (NB the start end would have changed though)
|
||||
const codeWithExtraStartWhitespace = '\n\n\n' + code
|
||||
const { nonCodeMeta: nonCodeMeta2 } = parser_wasm(
|
||||
codeWithExtraStartWhitespace
|
||||
)
|
||||
const { nonCodeMeta: nonCodeMeta2 } = parse(codeWithExtraStartWhitespace)
|
||||
expect(nonCodeMeta2.nonCodeNodes[0].value).toStrictEqual(
|
||||
nonCodeMetaInstance.value
|
||||
)
|
||||
@ -1545,7 +1542,7 @@ const key = 'c'`
|
||||
|> close(%)
|
||||
`
|
||||
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
const indexOfSecondLineToExpression = 2
|
||||
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
|
||||
.nonCodeNodes
|
||||
@ -1569,7 +1566,7 @@ const key = 'c'`
|
||||
' |> rx(90, %)',
|
||||
].join('\n')
|
||||
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
|
||||
.nonCodeNodes
|
||||
expect(sketchNonCodeMeta[3]).toEqual({
|
||||
@ -1587,7 +1584,7 @@ const key = 'c'`
|
||||
describe('test UnaryExpression', () => {
|
||||
it('should parse a unary expression in simple var dec situation', () => {
|
||||
const code = `const myVar = -min(4, 100)`
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
||||
expect(myVarInit).toEqual({
|
||||
type: 'UnaryExpression',
|
||||
@ -1613,7 +1610,7 @@ describe('test UnaryExpression', () => {
|
||||
describe('testing nested call expressions', () => {
|
||||
it('callExp in a binExp in a callExp', () => {
|
||||
const code = 'const myVar = min(100, 1 + legLen(5, 3))'
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
||||
expect(myVarInit).toEqual({
|
||||
type: 'CallExpression',
|
||||
@ -1651,7 +1648,7 @@ describe('testing nested call expressions', () => {
|
||||
describe('should recognise callExpresions in binaryExpressions', () => {
|
||||
const code = "xLineTo(segEndX('seg02', %) + 1, %)"
|
||||
it('should recognise the callExp', () => {
|
||||
const { body } = parser_wasm(code)
|
||||
const { body } = parse(code)
|
||||
const callExpArgs = (body?.[0] as any).expression?.arguments
|
||||
expect(callExpArgs).toEqual([
|
||||
{
|
||||
@ -1690,8 +1687,7 @@ describe('parsing errors', () => {
|
||||
|
||||
let _theError
|
||||
try {
|
||||
const result = expect(parser_wasm(code))
|
||||
console.log('result', result)
|
||||
const result = expect(parse(code))
|
||||
} catch (e) {
|
||||
_theError = e
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
import { Program } from './abstractSyntaxTreeTypes'
|
||||
import { parse_js } from '../wasm-lib/pkg/wasm_lib'
|
||||
import { initPromise } from './rust'
|
||||
import { Token } from './tokeniser'
|
||||
import { KCLError } from './errors'
|
||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||
|
||||
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
||||
ranges.map(([start, end]) => [start, end])
|
||||
|
||||
export const parser_wasm = (code: string): Program => {
|
||||
try {
|
||||
const program: Program = parse_js(code)
|
||||
return program
|
||||
} catch (e: any) {
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
const kclError = new KCLError(
|
||||
parsed.kind,
|
||||
parsed.msg,
|
||||
rangeTypeFix(parsed.sourceRanges)
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
throw kclError
|
||||
}
|
||||
}
|
||||
|
||||
export async function asyncParser(code: string): Promise<Program> {
|
||||
await initPromise
|
||||
try {
|
||||
const program: Program = parse_js(code)
|
||||
return program
|
||||
} catch (e: any) {
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
const kclError = new KCLError(
|
||||
parsed.kind,
|
||||
parsed.msg,
|
||||
rangeTypeFix(parsed.sourceRanges)
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
throw kclError
|
||||
}
|
||||
}
|
||||
|
||||
export function rangeOfToken(token: Token | undefined): [number, number][] {
|
||||
return token === undefined ? [] : [[token.start, token.end]]
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
export type { Value } from '../wasm-lib/kcl/bindings/Value'
|
||||
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
|
||||
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
|
||||
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
|
||||
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
|
||||
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
|
||||
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
|
||||
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'
|
||||
export type { BinaryExpression } from '../wasm-lib/kcl/bindings/BinaryExpression'
|
||||
export type { ReturnStatement } from '../wasm-lib/kcl/bindings/ReturnStatement'
|
||||
export type { ExpressionStatement } from '../wasm-lib/kcl/bindings/ExpressionStatement'
|
||||
export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
|
||||
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
|
||||
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
|
||||
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
|
||||
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
|
||||
|
||||
export type SyntaxType =
|
||||
| 'Program'
|
||||
| 'ExpressionStatement'
|
||||
| 'BinaryExpression'
|
||||
| 'CallExpression'
|
||||
| 'Identifier'
|
||||
| 'ReturnStatement'
|
||||
| 'VariableDeclaration'
|
||||
| 'VariableDeclarator'
|
||||
| 'MemberExpression'
|
||||
| 'ArrayExpression'
|
||||
| 'ObjectExpression'
|
||||
| 'ObjectProperty'
|
||||
| 'FunctionExpression'
|
||||
| 'PipeExpression'
|
||||
| 'PipeSubstitution'
|
||||
| 'Literal'
|
||||
| 'NonCodeNode'
|
||||
| 'UnaryExpression'
|
@ -1,5 +1,4 @@
|
||||
import { parser_wasm } from './abstractSyntaxTree'
|
||||
import { initPromise } from './rust'
|
||||
import { parse, initPromise } from './wasm'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
@ -8,12 +7,13 @@ describe('testing artifacts', () => {
|
||||
// Enable rotations #152
|
||||
test('sketch artifacts', async () => {
|
||||
const code = `
|
||||
const mySketch001 = startSketchAt([0, 0])
|
||||
const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
// |> rx(45, %)
|
||||
show(mySketch001)`
|
||||
const programMemory = await enginelessExecutor(parser_wasm(code))
|
||||
const programMemory = await enginelessExecutor(parse(code))
|
||||
// @ts-ignore
|
||||
const shown = programMemory?.return?.map(
|
||||
// @ts-ignore
|
||||
@ -28,7 +28,7 @@ show(mySketch001)`
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [21, 42],
|
||||
sourceRange: [46, 71],
|
||||
},
|
||||
},
|
||||
value: [
|
||||
@ -38,7 +38,7 @@ show(mySketch001)`
|
||||
to: [-1.59, -1.54],
|
||||
from: [0, 0],
|
||||
__geoMeta: {
|
||||
sourceRange: [48, 73],
|
||||
sourceRange: [77, 102],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -48,7 +48,7 @@ show(mySketch001)`
|
||||
from: [-1.59, -1.54],
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
sourceRange: [79, 103],
|
||||
sourceRange: [108, 132],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -56,20 +56,22 @@ show(mySketch001)`
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
id: expect.any(String),
|
||||
__meta: [{ sourceRange: [21, 42] }],
|
||||
planeId: expect.any(String),
|
||||
__meta: [{ sourceRange: [46, 71] }],
|
||||
},
|
||||
])
|
||||
})
|
||||
test('extrude artifacts', async () => {
|
||||
// Enable rotations #152
|
||||
const code = `
|
||||
const mySketch001 = startSketchAt([0, 0])
|
||||
const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
// |> rx(45, %)
|
||||
|> extrude(2, %)
|
||||
show(mySketch001)`
|
||||
const programMemory = await enginelessExecutor(parser_wasm(code))
|
||||
const programMemory = await enginelessExecutor(parse(code))
|
||||
// @ts-ignore
|
||||
const shown = programMemory?.return?.map(
|
||||
// @ts-ignore
|
||||
@ -83,7 +85,7 @@ show(mySketch001)`
|
||||
height: 2,
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
__meta: [{ sourceRange: [21, 42] }],
|
||||
__meta: [{ sourceRange: [46, 71] }],
|
||||
},
|
||||
])
|
||||
})
|
||||
@ -91,7 +93,8 @@ show(mySketch001)`
|
||||
// Enable rotations #152
|
||||
// TODO #153 in order for getExtrudeWallTransform to work we need to query the engine for the location of a face.
|
||||
const code = `
|
||||
const sk1 = startSketchAt([0, 0])
|
||||
const sk1 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-2.5, 0], %)
|
||||
|> lineTo({ to: [0, 10], tag: "p" }, %)
|
||||
|> lineTo([2.5, 0], %)
|
||||
@ -100,7 +103,8 @@ const sk1 = startSketchAt([0, 0])
|
||||
// |> ry(5, %)
|
||||
const theExtrude = extrude(2, sk1)
|
||||
// const theTransf = getExtrudeWallTransform('p', theExtrude)
|
||||
const sk2 = startSketchAt([0, 0])
|
||||
const sk2 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-2.5, 0], %)
|
||||
|> lineTo({ to: [0, 3], tag: "p" }, %)
|
||||
|> lineTo([2.5, 0], %)
|
||||
@ -109,7 +113,7 @@ const sk2 = startSketchAt([0, 0])
|
||||
|
||||
|
||||
show(theExtrude, sk2)`
|
||||
const programMemory = await enginelessExecutor(parser_wasm(code))
|
||||
const programMemory = await enginelessExecutor(parse(code))
|
||||
// @ts-ignore
|
||||
const geos = programMemory?.return?.map(
|
||||
// @ts-ignore
|
||||
@ -123,7 +127,7 @@ show(theExtrude, sk2)`
|
||||
height: 2,
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
__meta: [{ sourceRange: [13, 34] }],
|
||||
__meta: [{ sourceRange: [38, 63] }],
|
||||
},
|
||||
{
|
||||
type: 'ExtrudeGroup',
|
||||
@ -132,7 +136,7 @@ show(theExtrude, sk2)`
|
||||
height: 2,
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
__meta: [{ sourceRange: [302, 323] }],
|
||||
__meta: [{ sourceRange: [356, 381] }],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
@ -1,10 +1,7 @@
|
||||
import fs from 'node:fs'
|
||||
|
||||
import { parser_wasm } from './abstractSyntaxTree'
|
||||
import { ProgramMemory, SketchGroup } from './executor'
|
||||
import { initPromise } from './rust'
|
||||
import { parse, ProgramMemory, SketchGroup, initPromise } from './wasm'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import { vi } from 'vitest'
|
||||
import { KCLError } from './errors'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
@ -44,7 +41,8 @@ const newVar = myVar + 1`
|
||||
expect(root.magicNum.value).toBe(69)
|
||||
})
|
||||
it('sketch declaration', async () => {
|
||||
let code = `const mySketch = startSketchAt([0,0])
|
||||
let code = `const mySketch = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> lineTo({to: [0,2], tag: "myPath"}, %)
|
||||
|> lineTo([2,3], %)
|
||||
|> lineTo({ to: [5,-1], tag: "rightPath" }, %)
|
||||
@ -60,7 +58,7 @@ show(mySketch)
|
||||
to: [0, 2],
|
||||
from: [0, 0],
|
||||
__geoMeta: {
|
||||
sourceRange: [43, 80],
|
||||
sourceRange: [72, 109],
|
||||
id: expect.any(String),
|
||||
},
|
||||
name: 'myPath',
|
||||
@ -71,7 +69,7 @@ show(mySketch)
|
||||
from: [0, 2],
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
sourceRange: [86, 102],
|
||||
sourceRange: [115, 131],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -80,7 +78,7 @@ show(mySketch)
|
||||
to: [5, -1],
|
||||
from: [2, 3],
|
||||
__geoMeta: {
|
||||
sourceRange: [108, 151],
|
||||
sourceRange: [137, 180],
|
||||
id: expect.any(String),
|
||||
},
|
||||
name: 'rightPath',
|
||||
@ -90,8 +88,8 @@ show(mySketch)
|
||||
expect(_return).toEqual([
|
||||
{
|
||||
type: 'Identifier',
|
||||
start: 174,
|
||||
end: 182,
|
||||
start: 203,
|
||||
end: 211,
|
||||
name: 'mySketch',
|
||||
},
|
||||
])
|
||||
@ -135,7 +133,8 @@ show(mySketch)
|
||||
it('execute pipe sketch into call expression', async () => {
|
||||
// Enable rotations #152
|
||||
const code = [
|
||||
'const mySk1 = startSketchAt([0,0])',
|
||||
"const mySk1 = startSketchOn('XY')",
|
||||
' |> startProfileAt([0,0], %)',
|
||||
' |> lineTo([1,1], %)',
|
||||
' |> lineTo({to: [0, 1], tag: "myPath"}, %)',
|
||||
' |> lineTo([1,1], %)',
|
||||
@ -150,7 +149,7 @@ show(mySketch)
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [14, 34],
|
||||
sourceRange: [39, 63],
|
||||
},
|
||||
},
|
||||
value: [
|
||||
@ -160,7 +159,7 @@ show(mySketch)
|
||||
from: [0, 0],
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
sourceRange: [40, 56],
|
||||
sourceRange: [69, 85],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -169,7 +168,7 @@ show(mySketch)
|
||||
to: [0, 1],
|
||||
from: [1, 1],
|
||||
__geoMeta: {
|
||||
sourceRange: [62, 100],
|
||||
sourceRange: [91, 129],
|
||||
id: expect.any(String),
|
||||
},
|
||||
name: 'myPath',
|
||||
@ -180,7 +179,7 @@ show(mySketch)
|
||||
from: [0, 1],
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
sourceRange: [106, 122],
|
||||
sourceRange: [135, 151],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -188,7 +187,8 @@ show(mySketch)
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
id: expect.any(String),
|
||||
__meta: [{ sourceRange: [14, 34] }],
|
||||
planeId: expect.any(String),
|
||||
__meta: [{ sourceRange: [39, 63] }],
|
||||
})
|
||||
})
|
||||
it('execute array expression', async () => {
|
||||
@ -332,7 +332,8 @@ describe('testing math operators', () => {
|
||||
})
|
||||
it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => {
|
||||
const code = [
|
||||
'const part001 = startSketchAt([0, 0])',
|
||||
"const part001 = startSketchOn('XY')",
|
||||
' |> startProfileAt([0, 0], %)',
|
||||
'|> line([-2.21, -legLen(5, min(3, 999))], %)',
|
||||
].join('\n')
|
||||
const { root } = await exe(code)
|
||||
@ -344,7 +345,8 @@ describe('testing math operators', () => {
|
||||
it('test that % substitution feeds down CallExp->ArrExp->UnaryExp->CallExp', async () => {
|
||||
const code = [
|
||||
`const myVar = 3`,
|
||||
`const part001 = startSketchAt([0, 0])`,
|
||||
`const part001 = startSketchOn('XY')`,
|
||||
` |> startProfileAt([0, 0], %)`,
|
||||
` |> line({ to: [3, 4], tag: 'seg01' }, %)`,
|
||||
` |> line([`,
|
||||
` min(segLen('seg01', %), myVar),`,
|
||||
@ -380,7 +382,8 @@ describe('testing math operators', () => {
|
||||
describe('Testing Errors', () => {
|
||||
it('should throw an error when a variable is not defined', async () => {
|
||||
const code = `const myVar = 5
|
||||
const theExtrude = startSketchAt([0, 0])
|
||||
const theExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([-2.4, 5], %)
|
||||
|> line([-0.76], myVarZ, %)
|
||||
|> line([5,5], %)
|
||||
@ -391,7 +394,7 @@ show(theExtrude)`
|
||||
new KCLError(
|
||||
'undefined_value',
|
||||
'memory item key `myVarZ` is not defined',
|
||||
[[100, 106]]
|
||||
[[129, 135]]
|
||||
)
|
||||
)
|
||||
})
|
||||
@ -403,7 +406,7 @@ async function exe(
|
||||
code: string,
|
||||
programMemory: ProgramMemory = { root: {}, return: null }
|
||||
) {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
|
||||
const result = await enginelessExecutor(ast, programMemory)
|
||||
return result
|
||||
|
@ -1,81 +0,0 @@
|
||||
import { Program } from './abstractSyntaxTreeTypes'
|
||||
import {
|
||||
EngineCommandManager,
|
||||
ArtifactMap,
|
||||
SourceRangeMap,
|
||||
} from './std/engineConnection'
|
||||
import { ProgramReturn } from '../wasm-lib/kcl/bindings/ProgramReturn'
|
||||
import { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
|
||||
import { execute_wasm } from '../wasm-lib/pkg/wasm_lib'
|
||||
import { KCLError } from './errors'
|
||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||
import { rangeTypeFix } from './abstractSyntaxTree'
|
||||
|
||||
export type { SourceRange } from '../wasm-lib/kcl/bindings/SourceRange'
|
||||
export type { Position } from '../wasm-lib/kcl/bindings/Position'
|
||||
export type { Rotation } from '../wasm-lib/kcl/bindings/Rotation'
|
||||
export type { Path } from '../wasm-lib/kcl/bindings/Path'
|
||||
export type { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup'
|
||||
export type { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
|
||||
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
|
||||
|
||||
export type PathToNode = [string | number, string][]
|
||||
|
||||
interface Memory {
|
||||
[key: string]: MemoryItem
|
||||
}
|
||||
|
||||
export interface ProgramMemory {
|
||||
root: Memory
|
||||
return: ProgramReturn | null
|
||||
}
|
||||
|
||||
export const executor = async (
|
||||
node: Program,
|
||||
programMemory: ProgramMemory = { root: {}, return: null },
|
||||
engineCommandManager: EngineCommandManager,
|
||||
// work around while the gemotry is still be stored on the frontend
|
||||
// will be removed when the stream UI is added.
|
||||
tempMapCallback: (a: {
|
||||
artifactMap: ArtifactMap
|
||||
sourceRangeMap: SourceRangeMap
|
||||
}) => void = () => {}
|
||||
): Promise<ProgramMemory> => {
|
||||
engineCommandManager.startNewSession()
|
||||
const _programMemory = await _executor(
|
||||
node,
|
||||
programMemory,
|
||||
engineCommandManager
|
||||
)
|
||||
const { artifactMap, sourceRangeMap } =
|
||||
await engineCommandManager.waitForAllCommands(node, _programMemory)
|
||||
tempMapCallback({ artifactMap, sourceRangeMap })
|
||||
|
||||
engineCommandManager.endSession()
|
||||
return _programMemory
|
||||
}
|
||||
|
||||
export const _executor = async (
|
||||
node: Program,
|
||||
programMemory: ProgramMemory = { root: {}, return: null },
|
||||
engineCommandManager: EngineCommandManager
|
||||
): Promise<ProgramMemory> => {
|
||||
try {
|
||||
const memory: ProgramMemory = await execute_wasm(
|
||||
JSON.stringify(node),
|
||||
JSON.stringify(programMemory),
|
||||
engineCommandManager
|
||||
)
|
||||
return memory
|
||||
} catch (e: any) {
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
const kclError = new KCLError(
|
||||
parsed.kind,
|
||||
parsed.msg,
|
||||
rangeTypeFix(parsed.sourceRanges)
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
throw kclError
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
||||
import { parser_wasm } from './abstractSyntaxTree'
|
||||
import { initPromise } from './rust'
|
||||
import { Identifier } from './abstractSyntaxTreeTypes'
|
||||
import { Identifier, parse, initPromise } from './wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
@ -21,7 +19,7 @@ const sk3 = startSketchAt([0, 0])
|
||||
lineToSubstringIndex + subStr.length,
|
||||
]
|
||||
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const { node } = getNodeFromPath<any>(ast, nodePath)
|
||||
|
||||
@ -46,7 +44,7 @@ const b1 = cube([0,0], 10)`
|
||||
subStrIndex + 'pos'.length,
|
||||
]
|
||||
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const node = getNodeFromPath<Identifier>(ast, nodePath).node
|
||||
|
||||
@ -80,7 +78,7 @@ const b1 = cube([0,0], 10)`
|
||||
subStrIndex + 'scale'.length,
|
||||
]
|
||||
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const node = getNodeFromPath<Identifier>(ast, nodePath).node
|
||||
expect(nodePath).toEqual([
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { parser_wasm } from './abstractSyntaxTree'
|
||||
import { parse, recast, initPromise } from './wasm'
|
||||
import {
|
||||
createLiteral,
|
||||
createIdentifier,
|
||||
@ -13,8 +13,6 @@ import {
|
||||
giveSketchFnCallTag,
|
||||
moveValueIntoNewVariable,
|
||||
} from './modifyAst'
|
||||
import { recast } from './recast'
|
||||
import { initPromise } from './rust'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
@ -111,8 +109,8 @@ describe('Testing addSketchTo', () => {
|
||||
'yz'
|
||||
)
|
||||
const str = recast(result.modifiedAst)
|
||||
expect(str).toBe(`const part001 = startSketchAt('default')
|
||||
|> ry(90, %)
|
||||
expect(str).toBe(`const part001 = startSketchOn('YZ')
|
||||
|> startProfileAt('default', %)
|
||||
|> line('default', %)
|
||||
show(part001)
|
||||
`)
|
||||
@ -126,7 +124,7 @@ function giveSketchFnCallTagTestHelper(
|
||||
// giveSketchFnCallTag inputs and outputs an ast, which is very verbose for testing
|
||||
// this wrapper changes the input and output to code
|
||||
// making it more of an integration test, but easier to read the test intention is the goal
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const start = code.indexOf(searchStr)
|
||||
const range: [number, number] = [start, start + searchStr.length]
|
||||
const { modifiedAst, tag, isTagExisting } = giveSketchFnCallTag(ast, range)
|
||||
@ -135,7 +133,8 @@ function giveSketchFnCallTagTestHelper(
|
||||
}
|
||||
|
||||
describe('Testing giveSketchFnCallTag', () => {
|
||||
const code = `const part001 = startSketchAt([0, 0])
|
||||
const code = `const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([-2.57, -0.13], %)
|
||||
|> line([0, 0.83], %)
|
||||
|> line([0.82, 0.34], %)
|
||||
@ -187,7 +186,8 @@ fn ghi = (x) => {
|
||||
const abc = 3
|
||||
const identifierGuy = 5
|
||||
const yo = 5 + 6
|
||||
const part001 = startSketchAt([-1.2, 4.83])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-1.2, 4.83], %)
|
||||
|> line([2.8, 0], %)
|
||||
|> angledLine([100 + 100, 3.09], %)
|
||||
|> angledLine([abc, 3.09], %)
|
||||
@ -197,7 +197,7 @@ const part001 = startSketchAt([-1.2, 4.83])
|
||||
const yo2 = hmm([identifierGuy + 5])
|
||||
show(part001)`
|
||||
it('should move a binary expression into a new variable', async () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('100 + 100') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
@ -211,7 +211,7 @@ show(part001)`
|
||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||
})
|
||||
it('should move a value into a new variable', async () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('2.8') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
@ -225,7 +225,7 @@ show(part001)`
|
||||
expect(newCode).toContain(`line([newVar, 0], %)`)
|
||||
})
|
||||
it('should move a callExpression into a new variable', async () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('def(')
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
@ -239,7 +239,7 @@ show(part001)`
|
||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||
})
|
||||
it('should move a binary expression with call expression into a new variable', async () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('jkl(') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
@ -253,7 +253,7 @@ show(part001)`
|
||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||
})
|
||||
it('should move a identifier into a new variable', async () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('identifierGuy +') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
|
@ -14,14 +14,15 @@ import {
|
||||
ObjectExpression,
|
||||
UnaryExpression,
|
||||
BinaryExpression,
|
||||
} from './abstractSyntaxTreeTypes'
|
||||
PathToNode,
|
||||
ProgramMemory,
|
||||
} from './wasm'
|
||||
import {
|
||||
findAllPreviousVariables,
|
||||
getNodeFromPath,
|
||||
getNodePathFromSourceRange,
|
||||
isNodeSafeToReplace,
|
||||
} from './queryAst'
|
||||
import { PathToNode, ProgramMemory } from './executor'
|
||||
import {
|
||||
addTagForSketchOnFace,
|
||||
getFirstArg,
|
||||
@ -31,21 +32,26 @@ import { isLiteralArrayOrStatic } from './std/sketchcombos'
|
||||
|
||||
export function addStartSketch(
|
||||
node: Program,
|
||||
axis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz',
|
||||
start: [number, number],
|
||||
end: [number, number]
|
||||
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
||||
const _node = { ...node }
|
||||
const _name = findUniqueName(node, 'part')
|
||||
|
||||
const startSketchAt = createCallExpression('startSketchAt', [
|
||||
const startSketchOn = createCallExpressionStdLib('startSketchOn', [
|
||||
createLiteral(axis.toUpperCase()),
|
||||
])
|
||||
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
||||
createArrayExpression([createLiteral(start[0]), createLiteral(start[1])]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
const initialLineTo = createCallExpression('line', [
|
||||
createArrayExpression([createLiteral(end[0]), createLiteral(end[1])]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
|
||||
const pipeBody = [startSketchAt, initialLineTo]
|
||||
const pipeBody = [startSketchOn, startProfileAt, initialLineTo]
|
||||
|
||||
const variableDeclaration = createVariableDeclaration(
|
||||
_name,
|
||||
@ -78,11 +84,11 @@ export function addSketchTo(
|
||||
const _node = { ...node }
|
||||
const _name = name || findUniqueName(node, 'part')
|
||||
|
||||
const startSketchAt = createCallExpressionStdLib('startSketchAt', [
|
||||
createLiteral('default'),
|
||||
const startSketchOn = createCallExpressionStdLib('startSketchOn', [
|
||||
createLiteral(axis.toUpperCase()),
|
||||
])
|
||||
const rotate = createCallExpression(axis === 'xz' ? 'rx' : 'ry', [
|
||||
createLiteral(90),
|
||||
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
||||
createLiteral('default'),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
const initialLineTo = createCallExpressionStdLib('line', [
|
||||
@ -90,10 +96,7 @@ export function addSketchTo(
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
|
||||
const pipeBody =
|
||||
axis !== 'xy'
|
||||
? [startSketchAt, rotate, initialLineTo]
|
||||
: [startSketchAt, initialLineTo]
|
||||
const pipeBody = [startSketchOn, startProfileAt, initialLineTo]
|
||||
|
||||
const variableDeclaration = createVariableDeclaration(
|
||||
_name,
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { parser_wasm } from './abstractSyntaxTree'
|
||||
import { parse, recast, initPromise } from './wasm'
|
||||
import {
|
||||
findAllPreviousVariables,
|
||||
isNodeSafeToReplace,
|
||||
isTypeInValue,
|
||||
getNodePathFromSourceRange,
|
||||
} from './queryAst'
|
||||
import { initPromise } from './rust'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import {
|
||||
createArrayExpression,
|
||||
@ -13,7 +12,6 @@ import {
|
||||
createLiteral,
|
||||
createPipeSubstitution,
|
||||
} from './modifyAst'
|
||||
import { recast } from './recast'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
@ -28,7 +26,8 @@ const halfArmAngle = armAngle / 2
|
||||
const arrExpShouldNotBeIncluded = [1, 2, 3]
|
||||
const objExpShouldNotBeIncluded = { a: 1, b: 2, c: 3 }
|
||||
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> yLineTo(1, %)
|
||||
|> xLine(3.84, %) // selection-range-7ish-before-this
|
||||
|
||||
@ -36,7 +35,7 @@ const variableBelowShouldNotBeIncluded = 3
|
||||
|
||||
show(part001)`
|
||||
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
|
||||
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
||||
@ -59,7 +58,8 @@ show(part001)`
|
||||
})
|
||||
|
||||
describe('testing argIsNotIdentifier', () => {
|
||||
const code = `const part001 = startSketchAt([-1.2, 4.83])
|
||||
const code = `const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-1.2, 4.83], %)
|
||||
|> line([2.8, 0], %)
|
||||
|> angledLine([100 + 100, 3.09], %)
|
||||
|> angledLine([abc, 3.09], %)
|
||||
@ -70,7 +70,7 @@ const yo = 5 + 6
|
||||
const yo2 = hmm([identifierGuy + 5])
|
||||
show(part001)`
|
||||
it('find a safe binaryExpression', () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const rangeStart = code.indexOf('100 + 100') + 2
|
||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
||||
expect(result.isSafe).toBe(true)
|
||||
@ -84,7 +84,7 @@ show(part001)`
|
||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||
})
|
||||
it('find a safe Identifier', () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const rangeStart = code.indexOf('abc')
|
||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
||||
expect(result.isSafe).toBe(true)
|
||||
@ -92,7 +92,7 @@ show(part001)`
|
||||
expect(code.slice(result.value.start, result.value.end)).toBe('abc')
|
||||
})
|
||||
it('find a safe CallExpression', () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const rangeStart = code.indexOf('def')
|
||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
||||
expect(result.isSafe).toBe(true)
|
||||
@ -106,7 +106,7 @@ show(part001)`
|
||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||
})
|
||||
it('find an UNsafe CallExpression, as it has a PipeSubstitution', () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const rangeStart = code.indexOf('ghi')
|
||||
const range: [number, number] = [rangeStart, rangeStart]
|
||||
const result = isNodeSafeToReplace(ast, range)
|
||||
@ -115,7 +115,7 @@ show(part001)`
|
||||
expect(code.slice(result.value.start, result.value.end)).toBe('ghi(%)')
|
||||
})
|
||||
it('find an UNsafe Identifier, as it is a callee', () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const rangeStart = code.indexOf('ine([2.8,')
|
||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
||||
expect(result.isSafe).toBe(false)
|
||||
@ -125,7 +125,7 @@ show(part001)`
|
||||
)
|
||||
})
|
||||
it("find a safe BinaryExpression that's assigned to a variable", () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const rangeStart = code.indexOf('5 + 6') + 1
|
||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
||||
expect(result.isSafe).toBe(true)
|
||||
@ -139,7 +139,7 @@ show(part001)`
|
||||
expect(outCode).toContain(`const yo = replaceName`)
|
||||
})
|
||||
it('find a safe BinaryExpression that has a CallExpression within', () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const rangeStart = code.indexOf('jkl') + 1
|
||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
||||
expect(result.isSafe).toBe(true)
|
||||
@ -155,7 +155,7 @@ show(part001)`
|
||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||
})
|
||||
it('find a safe BinaryExpression within a CallExpression', () => {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const rangeStart = code.indexOf('identifierGuy') + 1
|
||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
||||
expect(result.isSafe).toBe(true)
|
||||
@ -196,14 +196,15 @@ show(part001)`
|
||||
})
|
||||
|
||||
describe('testing getNodePathFromSourceRange', () => {
|
||||
const code = `const part001 = startSketchAt([0.39, -0.05])
|
||||
const code = `const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.39, -0.05], %)
|
||||
|> line([0.94, 2.61], %)
|
||||
|> line([-0.21, -1.4], %)
|
||||
show(part001)`
|
||||
it('finds the second line when cursor is put at the end', () => {
|
||||
const searchLn = `line([0.94, 2.61], %)`
|
||||
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
||||
expect(result).toEqual([
|
||||
['body', ''],
|
||||
@ -212,13 +213,13 @@ show(part001)`
|
||||
[0, 'index'],
|
||||
['init', ''],
|
||||
['body', 'PipeExpression'],
|
||||
[1, 'index'],
|
||||
[2, 'index'],
|
||||
])
|
||||
})
|
||||
it('finds the last line when cursor is put at the end', () => {
|
||||
const searchLn = `line([-0.21, -1.4], %)`
|
||||
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
||||
const expected = [
|
||||
['body', ''],
|
||||
@ -227,7 +228,7 @@ show(part001)`
|
||||
[0, 'index'],
|
||||
['init', ''],
|
||||
['body', 'PipeExpression'],
|
||||
[2, 'index'],
|
||||
[3, 'index'],
|
||||
]
|
||||
expect(result).toEqual(expected)
|
||||
// expect similar result for start of line
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { PathToNode, ProgramMemory, SketchGroup, SourceRange } from './executor'
|
||||
import { Selection, ToolTip } from '../useStore'
|
||||
import {
|
||||
BinaryExpression,
|
||||
@ -10,7 +9,11 @@ import {
|
||||
VariableDeclaration,
|
||||
ReturnStatement,
|
||||
ArrayExpression,
|
||||
} from './abstractSyntaxTreeTypes'
|
||||
PathToNode,
|
||||
ProgramMemory,
|
||||
SketchGroup,
|
||||
SourceRange,
|
||||
} from './wasm'
|
||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
|
||||
import { getAngle } from '../lib/utils'
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { recast } from './recast'
|
||||
import { parser_wasm } from './abstractSyntaxTree'
|
||||
import { Program } from './abstractSyntaxTreeTypes'
|
||||
import { parse, Program, recast, initPromise } from './wasm'
|
||||
import fs from 'node:fs'
|
||||
import { initPromise } from './rust'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
@ -366,6 +363,6 @@ describe('it recasts binary expression using brackets where needed', () => {
|
||||
// helpers
|
||||
|
||||
function code2ast(code: string): { ast: Program } {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
return { ast }
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
import { Program } from './abstractSyntaxTreeTypes'
|
||||
import { recast_wasm } from '../wasm-lib/pkg/wasm_lib'
|
||||
|
||||
export const recast = (ast: Program): string => {
|
||||
try {
|
||||
const s: string = recast_wasm(JSON.stringify(ast))
|
||||
return s
|
||||
} catch (e) {
|
||||
// TODO: do something real with the error.
|
||||
console.log('recast', e)
|
||||
throw e
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import init from '../wasm-lib/pkg/wasm_lib'
|
||||
|
||||
const initialise = async () => {
|
||||
const baseUrl =
|
||||
typeof window === 'undefined'
|
||||
? 'http://127.0.0.1:3000'
|
||||
: window.location.origin.includes('tauri://localhost')
|
||||
? 'tauri://localhost'
|
||||
: window.location.origin.includes('localhost')
|
||||
? 'http://localhost:3000'
|
||||
: window.location.origin && window.location.origin !== 'null'
|
||||
? window.location.origin
|
||||
: 'http://localhost:3000'
|
||||
const fullUrl = baseUrl + '/wasm_lib_bg.wasm'
|
||||
const input = await fetch(fullUrl)
|
||||
const buffer = await input.arrayBuffer()
|
||||
return init(buffer)
|
||||
}
|
||||
|
||||
export const initPromise = initialise()
|
@ -1,4 +1,9 @@
|
||||
import { ProgramMemory, SourceRange } from 'lang/executor'
|
||||
import {
|
||||
ProgramMemory,
|
||||
SourceRange,
|
||||
Program,
|
||||
VariableDeclarator,
|
||||
} from 'lang/wasm'
|
||||
import { Selections } from 'useStore'
|
||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_CONNECTION_TIMEOUT_MS } from 'env'
|
||||
import { Models } from '@kittycad/lib'
|
||||
@ -6,7 +11,6 @@ import { exportSave } from 'lib/exportSave'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import * as Sentry from '@sentry/react'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
import { Program, VariableDeclarator } from 'lang/abstractSyntaxTreeTypes'
|
||||
|
||||
let lastMessage = ''
|
||||
|
||||
@ -356,6 +360,11 @@ export class EngineConnection {
|
||||
if (this.shouldTrace()) {
|
||||
iceSpan.resolve?.()
|
||||
}
|
||||
} else if (this.pc?.iceConnectionState === 'failed') {
|
||||
// failed is a terminal state; let's explicitly kill the
|
||||
// connection to the server at this point.
|
||||
console.log('failed to negotiate ice connection; restarting')
|
||||
this.close()
|
||||
}
|
||||
})
|
||||
|
||||
@ -487,9 +496,11 @@ export class EngineConnection {
|
||||
|
||||
this.onDataChannelOpen(this)
|
||||
|
||||
this.onEngineConnectionOpen(this)
|
||||
this.ready = true
|
||||
this.connecting = false
|
||||
// Do this after we set the connection is ready to avoid errors when
|
||||
// we try to send messages before the connection is ready.
|
||||
this.onEngineConnectionOpen(this)
|
||||
})
|
||||
|
||||
this.unreliableDataChannel.addEventListener('close', (event) => {
|
||||
@ -582,6 +593,9 @@ export class EngineCommandManager {
|
||||
outSequence = 1
|
||||
inSequence = 1
|
||||
engineConnection?: EngineConnection
|
||||
// Folks should realize that wait for ready does not get called _everytime_
|
||||
// the connection resets and restarts, it only gets called the first time.
|
||||
// Be careful what you put here.
|
||||
waitForReady: Promise<void> = new Promise(() => {})
|
||||
private resolveReady = () => {}
|
||||
|
||||
@ -595,19 +609,36 @@ export class EngineCommandManager {
|
||||
[localUnsubscribeId: string]: (a: any) => void
|
||||
}
|
||||
} = {} as any
|
||||
constructor({
|
||||
|
||||
constructor() {
|
||||
this.engineConnection = undefined
|
||||
}
|
||||
|
||||
start({
|
||||
setMediaStream,
|
||||
setIsStreamReady,
|
||||
width,
|
||||
height,
|
||||
executeCode,
|
||||
token,
|
||||
}: {
|
||||
setMediaStream: (stream: MediaStream) => void
|
||||
setIsStreamReady: (isStreamReady: boolean) => void
|
||||
width: number
|
||||
height: number
|
||||
executeCode: (code?: string, force?: boolean) => void
|
||||
token?: string
|
||||
}) {
|
||||
if (width === 0 || height === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// If we already have an engine connection, just need to resize the stream.
|
||||
if (this.engineConnection) {
|
||||
this.handleResize({ streamWidth: width, streamHeight: height })
|
||||
return
|
||||
}
|
||||
|
||||
this.waitForReady = new Promise((resolve) => {
|
||||
this.resolveReady = resolve
|
||||
})
|
||||
@ -618,6 +649,32 @@ export class EngineCommandManager {
|
||||
onEngineConnectionOpen: () => {
|
||||
this.resolveReady()
|
||||
setIsStreamReady(true)
|
||||
|
||||
// Make the axis gizmo.
|
||||
// We do this after the connection opened to avoid a race condition.
|
||||
// Connected opened is the last thing that happens when the stream
|
||||
// is ready.
|
||||
// We also do this here because we want to ensure we create the gizmo
|
||||
// and execute the code everytime the stream is restarted.
|
||||
const gizmoId = uuidv4()
|
||||
this.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: gizmoId,
|
||||
cmd: {
|
||||
type: 'make_axes_gizmo',
|
||||
clobber: false,
|
||||
// If true, axes gizmo will be placed in the corner of the screen.
|
||||
// If false, it will be placed at the origin of the scene.
|
||||
gizmo_mode: true,
|
||||
},
|
||||
})
|
||||
|
||||
// We execute the code here to make sure if the stream was to
|
||||
// restart in a session, we want to make sure to execute the code.
|
||||
// We force it to re-execute the code because we want to make sure
|
||||
// the code is executed everytime the stream is restarted.
|
||||
// We pass undefined for the code so it reads from the current state.
|
||||
executeCode(undefined, true)
|
||||
},
|
||||
onClose: () => {
|
||||
setIsStreamReady(false)
|
||||
@ -668,7 +725,11 @@ export class EngineCommandManager {
|
||||
message.request_id
|
||||
) {
|
||||
this.handleModelingCommand(message.resp, message.request_id)
|
||||
} else if (!message.success && message.request_id) {
|
||||
} else if (
|
||||
!message.success &&
|
||||
message.request_id &&
|
||||
this.artifactMap[message.request_id]
|
||||
) {
|
||||
this.handleFailedModelingCommand(message)
|
||||
}
|
||||
}
|
||||
@ -689,6 +750,30 @@ export class EngineCommandManager {
|
||||
|
||||
this.engineConnection?.connect()
|
||||
}
|
||||
handleResize({
|
||||
streamWidth,
|
||||
streamHeight,
|
||||
}: {
|
||||
streamWidth: number
|
||||
streamHeight: number
|
||||
}) {
|
||||
console.log('handleResize', streamWidth, streamHeight)
|
||||
if (!this.engineConnection?.isReady()) {
|
||||
return
|
||||
}
|
||||
|
||||
const resizeCmd: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'reconfigure_stream',
|
||||
width: streamWidth,
|
||||
height: streamHeight,
|
||||
fps: 60,
|
||||
},
|
||||
}
|
||||
this.engineConnection?.send(resizeCmd)
|
||||
}
|
||||
handleModelingCommand(message: WebSocketResponse, id: string) {
|
||||
if (message.type !== 'modeling') {
|
||||
return
|
||||
@ -854,6 +939,14 @@ export class EngineCommandManager {
|
||||
})
|
||||
}
|
||||
sendSceneCommand(command: EngineCommand): Promise<any> {
|
||||
if (this.engineConnection === undefined) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
if (!this.engineConnection?.isReady()) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
if (
|
||||
command.type === 'modeling_cmd_req' &&
|
||||
command.cmd.type !== lastMessage
|
||||
@ -861,9 +954,6 @@ export class EngineCommandManager {
|
||||
console.log('sending command', command.cmd.type)
|
||||
lastMessage = command.cmd.type
|
||||
}
|
||||
if (!this.engineConnection?.isReady()) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (command.type !== 'modeling_cmd_req') return Promise.resolve()
|
||||
const cmd = command.cmd
|
||||
if (
|
||||
@ -905,6 +995,9 @@ export class EngineCommandManager {
|
||||
range: SourceRange
|
||||
command: EngineCommand | string
|
||||
}): Promise<any> {
|
||||
if (this.engineConnection === undefined) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
this.sourceRangeMap[id] = range
|
||||
|
||||
if (!this.engineConnection?.isReady()) {
|
||||
@ -950,6 +1043,9 @@ export class EngineCommandManager {
|
||||
rangeStr: string,
|
||||
commandStr: string
|
||||
): Promise<any> {
|
||||
if (this.engineConnection === undefined) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (id === undefined) {
|
||||
throw new Error('id is undefined')
|
||||
}
|
||||
@ -1000,6 +1096,9 @@ export class EngineCommandManager {
|
||||
}
|
||||
}
|
||||
private async fixIdMappings(ast: Program, programMemory: ProgramMemory) {
|
||||
if (this.engineConnection === undefined) {
|
||||
return
|
||||
}
|
||||
/* This is a temporary solution since the cmd_ids that are sent through when
|
||||
sending 'extend_path' ids are not used as the segment ids.
|
||||
|
||||
@ -1079,3 +1178,5 @@ export class EngineCommandManager {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const engineCommandManager = new EngineCommandManager()
|
||||
|
@ -6,13 +6,9 @@ import {
|
||||
getXComponent,
|
||||
addCloseToPipe,
|
||||
} from './sketch'
|
||||
import { parser_wasm } from '../abstractSyntaxTree'
|
||||
import { parse, recast, initPromise } from '../wasm'
|
||||
import { getNodePathFromSourceRange } from '../queryAst'
|
||||
import { recast } from '../recast'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
import { initPromise } from '../rust'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
const eachQuad: [number, [number, number]][] = [
|
||||
[-315, [1, 1]],
|
||||
@ -29,6 +25,8 @@ const eachQuad: [number, [number, number]][] = [
|
||||
[675, [1, -1]],
|
||||
]
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
describe('testing getYComponent', () => {
|
||||
it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
|
||||
const expected: [number, number][] = []
|
||||
@ -98,7 +96,8 @@ describe('testing changeSketchArguments', () => {
|
||||
const lineAfterChange = 'lineTo([2, 3], %)'
|
||||
test('changeSketchArguments', async () => {
|
||||
// Enable rotations #152
|
||||
const genCode = (line: string) => `const mySketch001 = startSketchAt([0, 0])
|
||||
const genCode = (line: string) => `const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> ${line}
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
// |> rx(45, %)
|
||||
@ -106,7 +105,7 @@ show(mySketch001)
|
||||
`
|
||||
const code = genCode(lineToChange)
|
||||
const expectedCode = genCode(lineAfterChange)
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const sourceStart = code.indexOf(lineToChange)
|
||||
const { modifiedAst } = changeSketchArguments(
|
||||
@ -139,15 +138,16 @@ describe('testing addNewSketchLn', () => {
|
||||
test('addNewSketchLn', async () => {
|
||||
// Enable rotations #152
|
||||
const code = `
|
||||
const mySketch001 = startSketchAt([0, 0])
|
||||
const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
// |> rx(45, %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
show(mySketch001)`
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const sourceStart = code.indexOf(lineToChange)
|
||||
expect(sourceStart).toBe(66)
|
||||
expect(sourceStart).toBe(95)
|
||||
let { modifiedAst } = addNewSketchLn({
|
||||
node: ast,
|
||||
programMemory,
|
||||
@ -162,7 +162,8 @@ show(mySketch001)`
|
||||
],
|
||||
})
|
||||
// Enable rotations #152
|
||||
let expectedCode = `const mySketch001 = startSketchAt([0, 0])
|
||||
let expectedCode = `const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
// |> rx(45, %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
@ -183,7 +184,8 @@ show(mySketch001)
|
||||
],
|
||||
})
|
||||
|
||||
expectedCode = `const mySketch001 = startSketchAt([0, 0])
|
||||
expectedCode = `const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
// |> rx(45, %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
@ -198,14 +200,15 @@ describe('testing addTagForSketchOnFace', () => {
|
||||
it('needs to be in it', async () => {
|
||||
const originalLine = 'lineTo([-1.59, -1.54], %)'
|
||||
// Enable rotations #152
|
||||
const genCode = (line: string) => `const mySketch001 = startSketchAt([0, 0])
|
||||
const genCode = (line: string) => `const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
// |> rx(45, %)
|
||||
|> ${line}
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
show(mySketch001)
|
||||
`
|
||||
const code = genCode(originalLine)
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const sourceStart = code.indexOf(originalLine)
|
||||
const sourceRange: [number, number] = [
|
||||
|
@ -4,9 +4,6 @@ import {
|
||||
SketchGroup,
|
||||
SourceRange,
|
||||
PathToNode,
|
||||
MemoryItem,
|
||||
} from '../executor'
|
||||
import {
|
||||
Program,
|
||||
PipeExpression,
|
||||
CallExpression,
|
||||
@ -14,7 +11,7 @@ import {
|
||||
Value,
|
||||
Literal,
|
||||
VariableDeclaration,
|
||||
} from '../abstractSyntaxTreeTypes'
|
||||
} from '../wasm'
|
||||
import {
|
||||
getNodeFromPath,
|
||||
getNodeFromPathCurry,
|
||||
@ -38,7 +35,6 @@ import {
|
||||
findUniqueName,
|
||||
} from '../modifyAst'
|
||||
import { roundOff, getLength, getAngle } from '../../lib/utils'
|
||||
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
||||
import { perpendicularDistance } from 'sketch-helpers'
|
||||
|
||||
export type Coords2d = [number, number]
|
||||
|
@ -1,12 +1,9 @@
|
||||
import { parser_wasm } from '../abstractSyntaxTree'
|
||||
import { SketchGroup } from '../executor'
|
||||
import { parse, SketchGroup, recast, initPromise } from '../wasm'
|
||||
import {
|
||||
ConstraintType,
|
||||
getTransformInfos,
|
||||
transformAstSketchLines,
|
||||
} from './sketchcombos'
|
||||
import { recast } from '../recast'
|
||||
import { initPromise } from '../rust'
|
||||
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
||||
import { Selection } from '../../useStore'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
@ -31,7 +28,7 @@ async function testingSwapSketchFnCall({
|
||||
type: 'default',
|
||||
range: [startIndex, startIndex + callToSwap.length],
|
||||
}
|
||||
const ast = parser_wasm(inputCode)
|
||||
const ast = parse(inputCode)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const selections = {
|
||||
codeBasedSelections: [range],
|
||||
@ -55,7 +52,8 @@ async function testingSwapSketchFnCall({
|
||||
|
||||
describe('testing swaping out sketch calls with xLine/xLineTo', () => {
|
||||
const bigExampleArr = [
|
||||
`const part001 = startSketchAt([0, 0])`,
|
||||
`const part001 = startSketchOn('XY')`,
|
||||
` |> startProfileAt([0, 0], %)`,
|
||||
` |> lineTo({ to: [1, 1], tag: 'abc1' }, %)`,
|
||||
` |> line({ to: [-2.04, -0.7], tag: 'abc2' }, %)`,
|
||||
` |> angledLine({`,
|
||||
@ -280,7 +278,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari
|
||||
`const angledLineOfYLengthY = 0.89`,
|
||||
`const angledLineToXx = -1.86`,
|
||||
`const angledLineToYy = -0.76`,
|
||||
`const part001 = startSketchAt([0, 0])`,
|
||||
`const part001 = startSketchOn('XY')`,
|
||||
` |> startProfileAt([0, 0], %)`,
|
||||
// ` |> rx(90, %)`,
|
||||
` |> lineTo([1, 1], %)`,
|
||||
` |> line([lineX, 2.13], %)`,
|
||||
@ -374,14 +373,15 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari
|
||||
|
||||
describe('testing getSketchSegmentIndexFromSourceRange', () => {
|
||||
const code = `
|
||||
const part001 = startSketchAt([0, 0.04]) // segment-in-start
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0.04], %) // segment-in-start
|
||||
|> line([0, 0.4], %)
|
||||
|> xLine(3.48, %)
|
||||
|> line([2.14, 1.35], %) // normal-segment
|
||||
|> xLine(3.54, %)
|
||||
show(part001)`
|
||||
it('normal case works', async () => {
|
||||
const programMemory = await enginelessExecutor(parser_wasm(code))
|
||||
const programMemory = await enginelessExecutor(parse(code))
|
||||
const index = code.indexOf('// normal-segment') - 7
|
||||
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
||||
programMemory.root['part001'] as SketchGroup,
|
||||
@ -395,7 +395,7 @@ show(part001)`
|
||||
})
|
||||
})
|
||||
it('verify it works when the segment is in the `start` property', async () => {
|
||||
const programMemory = await enginelessExecutor(parser_wasm(code))
|
||||
const programMemory = await enginelessExecutor(parse(code))
|
||||
const index = code.indexOf('// segment-in-start') - 7
|
||||
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
||||
programMemory.root['part001'] as SketchGroup,
|
||||
|
@ -3,8 +3,10 @@ import {
|
||||
Program,
|
||||
VariableDeclarator,
|
||||
CallExpression,
|
||||
} from '../abstractSyntaxTreeTypes'
|
||||
import { SketchGroup, SourceRange, Path } from '../executor'
|
||||
SketchGroup,
|
||||
SourceRange,
|
||||
Path,
|
||||
} from '../wasm'
|
||||
|
||||
export function getSketchSegmentFromSourceRange(
|
||||
sketchGroup: SketchGroup,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { parser_wasm } from '../abstractSyntaxTree'
|
||||
import { Value } from '../abstractSyntaxTreeTypes'
|
||||
import { parse, Value, recast, initPromise } from '../wasm'
|
||||
import {
|
||||
getConstraintType,
|
||||
getTransformInfos,
|
||||
@ -8,10 +7,8 @@ import {
|
||||
ConstraintType,
|
||||
getConstraintLevelFromSourceRange,
|
||||
} from './sketchcombos'
|
||||
import { initPromise } from '../rust'
|
||||
import { Selections, ToolTip } from '../../useStore'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
import { recast } from '../../lang/recast'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
@ -63,7 +60,7 @@ describe('testing getConstraintType', () => {
|
||||
function getConstraintTypeFromSourceHelper(
|
||||
code: string
|
||||
): ReturnType<typeof getConstraintType> {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const args = (ast.body[0] as any).expression.arguments[0].elements as [
|
||||
Value,
|
||||
Value
|
||||
@ -74,7 +71,7 @@ function getConstraintTypeFromSourceHelper(
|
||||
function getConstraintTypeFromSourceHelper2(
|
||||
code: string
|
||||
): ReturnType<typeof getConstraintType> {
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const arg = (ast.body[0] as any).expression.arguments[0] as Value
|
||||
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
||||
return getConstraintType(arg, fnName)
|
||||
@ -95,7 +92,8 @@ const myVar2 = 5
|
||||
const myVar3 = 6
|
||||
const myAng = 40
|
||||
const myAng2 = 134
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([1, 3.82], %) // ln-should-get-tag
|
||||
|> lineTo([myVar, 1], %) // ln-lineTo-xAbsolute should use angleToMatchLengthX helper
|
||||
|> lineTo([1, myVar], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
|
||||
@ -131,7 +129,8 @@ const myVar2 = 5
|
||||
const myVar3 = 6
|
||||
const myAng = 40
|
||||
const myAng2 = 134
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|
||||
|> angledLineToX([
|
||||
-angleToMatchLengthX('seg01', myVar, %),
|
||||
@ -199,7 +198,7 @@ const part001 = startSketchAt([0, 0])
|
||||
show(part001)
|
||||
`
|
||||
it('should transform the ast', async () => {
|
||||
const ast = parser_wasm(inputScript)
|
||||
const ast = parse(inputScript)
|
||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||
.split('\n')
|
||||
.filter((ln) => ln.includes('//'))
|
||||
@ -234,7 +233,8 @@ describe('testing transformAstForSketchLines for vertical and horizontal constra
|
||||
const inputScript = `const myVar = 2
|
||||
const myVar2 = 12
|
||||
const myVar3 = -10
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
|> line([-6.28, 1.4], %) // select for horizontal constraint 1
|
||||
|> line([-1.07, myVar], %) // select for vertical constraint 1
|
||||
@ -262,7 +262,8 @@ show(part001)
|
||||
const expectModifiedScript = `const myVar = 2
|
||||
const myVar2 = 12
|
||||
const myVar3 = -10
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
|> xLine(-6.28, %) // select for horizontal constraint 1
|
||||
|> line([-1.07, myVar], %) // select for vertical constraint 1
|
||||
@ -286,7 +287,7 @@ const part001 = startSketchAt([0, 0])
|
||||
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
||||
show(part001)
|
||||
`
|
||||
const ast = parser_wasm(inputScript)
|
||||
const ast = parse(inputScript)
|
||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||
.split('\n')
|
||||
.filter((ln) => ln.includes('// select for horizontal constraint'))
|
||||
@ -320,7 +321,8 @@ show(part001)
|
||||
const expectModifiedScript = `const myVar = 2
|
||||
const myVar2 = 12
|
||||
const myVar3 = -10
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
|> line([-6.28, 1.4], %) // select for horizontal constraint 1
|
||||
|> yLine(myVar, %) // select for vertical constraint 1
|
||||
@ -344,7 +346,7 @@ const part001 = startSketchAt([0, 0])
|
||||
|> yLineTo(myVar, %) // select for vertical constraint 10
|
||||
show(part001)
|
||||
`
|
||||
const ast = parser_wasm(inputScript)
|
||||
const ast = parse(inputScript)
|
||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||
.split('\n')
|
||||
.filter((ln) => ln.includes('// select for vertical constraint'))
|
||||
@ -379,7 +381,8 @@ show(part001)
|
||||
describe('testing transformAstForSketchLines for vertical and horizontal distance constraints', () => {
|
||||
describe('testing setHorzDistance for line', () => {
|
||||
const inputScript = `const myVar = 1
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0.31, 1.67], %) // base selection
|
||||
|> line([0.45, 1.46], %)
|
||||
|> line([0.45, 1.46], %) // free
|
||||
@ -435,7 +438,7 @@ async function helperThing(
|
||||
linesOfInterest: string[],
|
||||
constraint: ConstraintType
|
||||
): Promise<string> {
|
||||
const ast = parser_wasm(inputScript)
|
||||
const ast = parse(inputScript)
|
||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||
.split('\n')
|
||||
.filter((ln) =>
|
||||
@ -480,7 +483,8 @@ const baseThickHalf = baseThick / 2
|
||||
const halfHeight = totalHeight / 2
|
||||
const halfArmAngle = armAngle / 2
|
||||
|
||||
const part001 = startSketchAt([-0.01, -0.05])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-0.01, -0.05], %)
|
||||
|> line([0.01, 0.94 + 0], %) // partial
|
||||
|> xLine(3.03, %) // partial
|
||||
|> angledLine({
|
||||
@ -498,7 +502,7 @@ const part001 = startSketchAt([-0.01, -0.05])
|
||||
|> xLine(-3.43 + 0, %) // full
|
||||
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full
|
||||
show(part001)`
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const constraintLevels: ReturnType<
|
||||
typeof getConstraintLevelFromSourceRange
|
||||
>[] = ['full', 'partial', 'free']
|
||||
|
@ -6,7 +6,9 @@ import {
|
||||
Value,
|
||||
BinaryPart,
|
||||
VariableDeclarator,
|
||||
} from '../abstractSyntaxTreeTypes'
|
||||
PathToNode,
|
||||
ProgramMemory,
|
||||
} from '../wasm'
|
||||
import {
|
||||
getNodeFromPath,
|
||||
getNodeFromPathCurry,
|
||||
@ -25,10 +27,8 @@ import {
|
||||
giveSketchFnCallTag,
|
||||
} from '../modifyAst'
|
||||
import { createFirstArg, getFirstArg, replaceSketchLine } from './sketch'
|
||||
import { PathToNode, ProgramMemory } from '../executor'
|
||||
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
||||
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
|
||||
import { MemoryItem } from 'wasm-lib/kcl/bindings/MemoryItem'
|
||||
|
||||
type LineInputsType =
|
||||
| 'xAbsolute'
|
||||
@ -1279,7 +1279,7 @@ export function getTransformInfos(
|
||||
}) as TransformInfo[]
|
||||
return theTransforms
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log('error', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { parser_wasm } from '../abstractSyntaxTree'
|
||||
import { parse, initPromise } from '../wasm'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
import { initPromise } from '../rust'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
describe('testing angledLineThatIntersects', () => {
|
||||
it('angledLineThatIntersects should intersect with another line', async () => {
|
||||
const code = (offset: string) => `const part001 = startSketchAt([0, 0])
|
||||
const code = (offset: string) => `const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo({to:[2, 2], tag: "yo"}, %)
|
||||
|> lineTo([3, 1], %)
|
||||
|> angledLineThatIntersects({
|
||||
@ -17,9 +17,9 @@ describe('testing angledLineThatIntersects', () => {
|
||||
}, %)
|
||||
const intersect = segEndX('yo2', part001)
|
||||
show(part001)`
|
||||
const { root } = await enginelessExecutor(parser_wasm(code('-1')))
|
||||
const { root } = await enginelessExecutor(parse(code('-1')))
|
||||
expect(root.intersect.value).toBe(1 + Math.sqrt(2))
|
||||
const { root: noOffset } = await enginelessExecutor(parser_wasm(code('0')))
|
||||
const { root: noOffset } = await enginelessExecutor(parse(code('0')))
|
||||
expect(noOffset.intersect.value).toBeCloseTo(1)
|
||||
})
|
||||
})
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { ProgramMemory, Path, SourceRange } from '../executor'
|
||||
import { Program, Value } from '../abstractSyntaxTreeTypes'
|
||||
import {
|
||||
ProgramMemory,
|
||||
Path,
|
||||
SourceRange,
|
||||
Program,
|
||||
Value,
|
||||
PathToNode,
|
||||
} from '../wasm'
|
||||
import { ToolTip } from '../../useStore'
|
||||
import { PathToNode } from '../executor'
|
||||
import { EngineCommandManager } from './engineConnection'
|
||||
|
||||
export interface InternalFirstArg {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { lexer, asyncLexer } from './tokeniser'
|
||||
import { initPromise } from './rust'
|
||||
import { lexer, initPromise } from './wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
|
||||
@ -10,9 +9,9 @@ describe('testing lexer', () => {
|
||||
const code3 = `const yo = 45 /* this is a comment
|
||||
const ya = 6 */
|
||||
const yi=45`
|
||||
expect(await asyncLexer(code)).toEqual(lexer(code))
|
||||
expect(await asyncLexer(code2)).toEqual(lexer(code2))
|
||||
expect(await asyncLexer(code3)).toEqual(lexer(code3))
|
||||
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([
|
||||
|
@ -1,28 +0,0 @@
|
||||
import { lexer_js } from '../wasm-lib/pkg/wasm_lib'
|
||||
import { initPromise } from './rust'
|
||||
import { Token } from '../wasm-lib/kcl/bindings/Token'
|
||||
|
||||
export type { Token } from '../wasm-lib/kcl/bindings/Token'
|
||||
|
||||
export async function asyncLexer(str: string): Promise<Token[]> {
|
||||
await initPromise
|
||||
try {
|
||||
const tokens: Token[] = lexer_js(str)
|
||||
return tokens
|
||||
} catch (e) {
|
||||
// TODO: do something real with the error.
|
||||
console.log('lexer', e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
export function lexer(str: string): Token[] {
|
||||
try {
|
||||
const tokens: Token[] = lexer_js(str)
|
||||
return tokens
|
||||
} catch (e) {
|
||||
// TODO: do something real with the error.
|
||||
console.log('lexer', e)
|
||||
throw e
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import { Selections, StoreState } from '../useStore'
|
||||
import { Program } from './abstractSyntaxTreeTypes'
|
||||
import { PathToNode } from './executor'
|
||||
import { Program, PathToNode } from './wasm'
|
||||
import { getNodeFromPath } from './queryAst'
|
||||
|
||||
export function updateCursors(
|
||||
|
222
src/lang/wasm.ts
Normal file
222
src/lang/wasm.ts
Normal file
@ -0,0 +1,222 @@
|
||||
import init, {
|
||||
parse_wasm,
|
||||
recast_wasm,
|
||||
execute_wasm,
|
||||
lexer_wasm,
|
||||
modify_ast_for_sketch_wasm,
|
||||
} from '../wasm-lib/pkg/wasm_lib'
|
||||
import { KCLError } from './errors'
|
||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||
import {
|
||||
EngineCommandManager,
|
||||
ArtifactMap,
|
||||
SourceRangeMap,
|
||||
} from './std/engineConnection'
|
||||
import { ProgramReturn } from '../wasm-lib/kcl/bindings/ProgramReturn'
|
||||
import { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
|
||||
import type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
import type { Token } from '../wasm-lib/kcl/bindings/Token'
|
||||
import { DefaultPlanes } from '../wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
|
||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
export type { Value } from '../wasm-lib/kcl/bindings/Value'
|
||||
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
|
||||
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
|
||||
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
|
||||
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
|
||||
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
|
||||
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
|
||||
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'
|
||||
export type { BinaryExpression } from '../wasm-lib/kcl/bindings/BinaryExpression'
|
||||
export type { ReturnStatement } from '../wasm-lib/kcl/bindings/ReturnStatement'
|
||||
export type { ExpressionStatement } from '../wasm-lib/kcl/bindings/ExpressionStatement'
|
||||
export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
|
||||
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
|
||||
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
|
||||
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
|
||||
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
|
||||
|
||||
export type SyntaxType =
|
||||
| 'Program'
|
||||
| 'ExpressionStatement'
|
||||
| 'BinaryExpression'
|
||||
| 'CallExpression'
|
||||
| 'Identifier'
|
||||
| 'ReturnStatement'
|
||||
| 'VariableDeclaration'
|
||||
| 'VariableDeclarator'
|
||||
| 'MemberExpression'
|
||||
| 'ArrayExpression'
|
||||
| 'ObjectExpression'
|
||||
| 'ObjectProperty'
|
||||
| 'FunctionExpression'
|
||||
| 'PipeExpression'
|
||||
| 'PipeSubstitution'
|
||||
| 'Literal'
|
||||
| 'NonCodeNode'
|
||||
| 'UnaryExpression'
|
||||
|
||||
export type { SourceRange } from '../wasm-lib/kcl/bindings/SourceRange'
|
||||
export type { Position } from '../wasm-lib/kcl/bindings/Position'
|
||||
export type { Rotation } from '../wasm-lib/kcl/bindings/Rotation'
|
||||
export type { Path } from '../wasm-lib/kcl/bindings/Path'
|
||||
export type { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup'
|
||||
export type { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
|
||||
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
|
||||
|
||||
// Initialise the wasm module.
|
||||
const initialise = async () => {
|
||||
const baseUrl =
|
||||
typeof window === 'undefined'
|
||||
? 'http://127.0.0.1:3000'
|
||||
: window.location.origin.includes('tauri://localhost')
|
||||
? 'tauri://localhost'
|
||||
: window.location.origin.includes('localhost')
|
||||
? 'http://localhost:3000'
|
||||
: window.location.origin && window.location.origin !== 'null'
|
||||
? window.location.origin
|
||||
: 'http://localhost:3000'
|
||||
const fullUrl = baseUrl + '/wasm_lib_bg.wasm'
|
||||
const input = await fetch(fullUrl)
|
||||
const buffer = await input.arrayBuffer()
|
||||
return init(buffer)
|
||||
}
|
||||
|
||||
export const initPromise = initialise()
|
||||
|
||||
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
||||
ranges.map(([start, end]) => [start, end])
|
||||
|
||||
export const parse = (code: string): Program => {
|
||||
try {
|
||||
const program: Program = parse_wasm(code)
|
||||
return program
|
||||
} catch (e: any) {
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
const kclError = new KCLError(
|
||||
parsed.kind,
|
||||
parsed.msg,
|
||||
rangeTypeFix(parsed.sourceRanges)
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
throw kclError
|
||||
}
|
||||
}
|
||||
|
||||
export type PathToNode = [string | number, string][]
|
||||
|
||||
interface Memory {
|
||||
[key: string]: MemoryItem
|
||||
}
|
||||
|
||||
export interface ProgramMemory {
|
||||
root: Memory
|
||||
return: ProgramReturn | null
|
||||
}
|
||||
|
||||
export const executor = async (
|
||||
node: Program,
|
||||
programMemory: ProgramMemory = { root: {}, return: null },
|
||||
engineCommandManager: EngineCommandManager,
|
||||
planes: DefaultPlanes,
|
||||
// work around while the gemotry is still be stored on the frontend
|
||||
// will be removed when the stream UI is added.
|
||||
tempMapCallback: (a: {
|
||||
artifactMap: ArtifactMap
|
||||
sourceRangeMap: SourceRangeMap
|
||||
}) => void = () => {}
|
||||
): Promise<ProgramMemory> => {
|
||||
engineCommandManager.startNewSession()
|
||||
const _programMemory = await _executor(
|
||||
node,
|
||||
programMemory,
|
||||
engineCommandManager,
|
||||
planes
|
||||
)
|
||||
const { artifactMap, sourceRangeMap } =
|
||||
await engineCommandManager.waitForAllCommands(node, _programMemory)
|
||||
tempMapCallback({ artifactMap, sourceRangeMap })
|
||||
|
||||
engineCommandManager.endSession()
|
||||
return _programMemory
|
||||
}
|
||||
|
||||
export const _executor = async (
|
||||
node: Program,
|
||||
programMemory: ProgramMemory = { root: {}, return: null },
|
||||
engineCommandManager: EngineCommandManager,
|
||||
planes: DefaultPlanes
|
||||
): Promise<ProgramMemory> => {
|
||||
try {
|
||||
const memory: ProgramMemory = await execute_wasm(
|
||||
JSON.stringify(node),
|
||||
JSON.stringify(programMemory),
|
||||
engineCommandManager,
|
||||
JSON.stringify(planes)
|
||||
)
|
||||
return memory
|
||||
} catch (e: any) {
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
const kclError = new KCLError(
|
||||
parsed.kind,
|
||||
parsed.msg,
|
||||
rangeTypeFix(parsed.sourceRanges)
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
throw kclError
|
||||
}
|
||||
}
|
||||
|
||||
export const recast = (ast: Program): string => {
|
||||
try {
|
||||
const s: string = recast_wasm(JSON.stringify(ast))
|
||||
return s
|
||||
} catch (e) {
|
||||
// TODO: do something real with the error.
|
||||
console.log('recast error', e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
export function lexer(str: string): Token[] {
|
||||
try {
|
||||
const tokens: Token[] = lexer_wasm(str)
|
||||
return tokens
|
||||
} catch (e) {
|
||||
// TODO: do something real with the error.
|
||||
console.log('lexer error', e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
export const modifyAstForSketch = async (
|
||||
engineCommandManager: EngineCommandManager,
|
||||
ast: Program,
|
||||
variableName: string,
|
||||
currentPlane: string,
|
||||
engineId: string
|
||||
): Promise<Program> => {
|
||||
try {
|
||||
const updatedAst: Program = await modify_ast_for_sketch_wasm(
|
||||
engineCommandManager,
|
||||
JSON.stringify(ast),
|
||||
variableName,
|
||||
JSON.stringify(currentPlane),
|
||||
engineId
|
||||
)
|
||||
|
||||
return updatedAst
|
||||
} catch (e: any) {
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
const kclError = new KCLError(
|
||||
parsed.kind,
|
||||
parsed.msg,
|
||||
rangeTypeFix(parsed.sourceRanges)
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
throw kclError
|
||||
}
|
||||
}
|
@ -1,19 +1,28 @@
|
||||
export const bracket = `// Material: 6061-T6 Aluminum
|
||||
const sigmaAllow = 35000 // psi
|
||||
const width = 9 // inch
|
||||
export const bracket = `const sigmaAllow = 15000 // psi
|
||||
const width = 11 // inch
|
||||
const p = 150 // Force on shelf - lbs
|
||||
const distance = 6 // inches
|
||||
const distance = 12 // inches
|
||||
const FOS = 2
|
||||
const thickness = sqrt(distance * p * FOS * 6 / ( sigmaAllow * width ))
|
||||
const filletR = thickness * 2
|
||||
const shelfMountL = 9
|
||||
const wallMountL = 8
|
||||
|
||||
const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
const thickness = sqrt(distance * p * FOS * 6 / sigmaAllow / width) // inches
|
||||
const bracket = startSketchAt([0, 0])
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, wallMountL], %)
|
||||
|> tangentalArc({
|
||||
radius: filletR,
|
||||
offset: 90
|
||||
}, %)
|
||||
|> line([-shelfMountL, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
|> line([-leg2 + thickness, 0], %)
|
||||
|> line([0, -leg1 + thickness], %)
|
||||
|> line([shelfMountL, 0], %)
|
||||
|> tangentalArc({
|
||||
radius: filletR - thickness,
|
||||
offset: -90
|
||||
}, %)
|
||||
|> line([0, -wallMountL], %)
|
||||
|> close(%)
|
||||
|> extrude(width, %)
|
||||
|
||||
|
@ -25,7 +25,7 @@ export async function exportSave(data: ArrayBuffer) {
|
||||
}
|
||||
|
||||
// Write the file.
|
||||
await writeBinaryFile(filePath, uintArray)
|
||||
await writeBinaryFile(filePath, file.contents)
|
||||
} else {
|
||||
// Download the file to the user's computer.
|
||||
// Now we need to download the files to the user's downloads folder.
|
||||
@ -39,6 +39,6 @@ export async function exportSave(data: ArrayBuffer) {
|
||||
}
|
||||
} catch (e) {
|
||||
// TODO: do something real with the error.
|
||||
console.log('export', e)
|
||||
console.log('export error', e)
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ export async function initializeProjectDirectory(directory: string) {
|
||||
try {
|
||||
docDirectory = await documentDir()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
console.log('error', e)
|
||||
docDirectory = await homeDir() // seems to work better on Linux
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { Program } from '../lang/abstractSyntaxTreeTypes'
|
||||
import { ProgramMemory, _executor } from '../lang/executor'
|
||||
import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm'
|
||||
import {
|
||||
EngineCommandManager,
|
||||
EngineCommand,
|
||||
} from '../lang/std/engineConnection'
|
||||
import { SourceRange } from 'lang/executor'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
type WebSocketResponse = Models['OkWebSocketResponseData_type']
|
||||
|
||||
@ -66,7 +65,11 @@ export async function enginelessExecutor(
|
||||
}) as any as EngineCommandManager
|
||||
await mockEngineCommandManager.waitForReady
|
||||
mockEngineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(ast, pm, mockEngineCommandManager)
|
||||
const programMemory = await _executor(ast, pm, mockEngineCommandManager, {
|
||||
xy: uuidv4(),
|
||||
yz: uuidv4(),
|
||||
xz: uuidv4(),
|
||||
})
|
||||
await mockEngineCommandManager.waitForAllCommands()
|
||||
return programMemory
|
||||
}
|
||||
@ -75,15 +78,21 @@ export async function executor(
|
||||
ast: Program,
|
||||
pm: ProgramMemory = { root: {}, return: null }
|
||||
): Promise<ProgramMemory> {
|
||||
const engineCommandManager = new EngineCommandManager({
|
||||
const engineCommandManager = new EngineCommandManager()
|
||||
engineCommandManager.start({
|
||||
setIsStreamReady: () => {},
|
||||
setMediaStream: () => {},
|
||||
width: 100,
|
||||
height: 100,
|
||||
width: 0,
|
||||
height: 0,
|
||||
executeCode: () => {},
|
||||
})
|
||||
await engineCommandManager.waitForReady
|
||||
engineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(ast, pm, engineCommandManager)
|
||||
const programMemory = await _executor(ast, pm, engineCommandManager, {
|
||||
xy: uuidv4(),
|
||||
yz: uuidv4(),
|
||||
xz: uuidv4(),
|
||||
})
|
||||
await engineCommandManager.waitForAllCommands(ast, programMemory)
|
||||
return programMemory
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { isOverlap, roundOff } from './utils'
|
||||
import { SourceRange } from '../lang/executor'
|
||||
import { SourceRange } from '../lang/wasm'
|
||||
|
||||
describe('testing isOverlapping', () => {
|
||||
testBothOrders([0, 3], [3, 10])
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { SourceRange } from '../lang/executor'
|
||||
import { SourceRange } from '../lang/wasm'
|
||||
|
||||
export function isOverlap(a: SourceRange, b: SourceRange) {
|
||||
const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a]
|
||||
|
@ -2,6 +2,7 @@ import { assign, createMachine } from 'xstate'
|
||||
import { CommandBarMeta } from '../lib/commands'
|
||||
import { Themes, getSystemTheme, setThemeClass } from '../lib/theme'
|
||||
import { CameraSystem, cameraSystems } from 'lib/cameraControls'
|
||||
import { Models } from '@kittycad/lib'
|
||||
|
||||
export const DEFAULT_PROJECT_NAME = 'project-$nnn'
|
||||
|
||||
@ -11,11 +12,11 @@ export enum UnitSystem {
|
||||
}
|
||||
|
||||
export const baseUnits = {
|
||||
imperial: ['in', 'ft'],
|
||||
imperial: ['in', 'ft', 'yd'],
|
||||
metric: ['mm', 'cm', 'm'],
|
||||
} as const
|
||||
|
||||
export type BaseUnit = 'in' | 'ft' | 'mm' | 'cm' | 'm'
|
||||
export type BaseUnit = Models['UnitLength_type']
|
||||
|
||||
export const baseUnitsUnion = Object.values(baseUnits).flatMap((v) => v)
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
|
||||
import { ActionButton } from '../../components/ActionButton'
|
||||
import { onboardingPaths, useDismiss, useNextClick } from '.'
|
||||
import {
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
onboardingPaths,
|
||||
useDismiss,
|
||||
useNextClick,
|
||||
} from '.'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
@ -25,14 +30,20 @@ function OnboardingWithNewFile() {
|
||||
}))
|
||||
const {
|
||||
settings: {
|
||||
context: { defaultDirectory, defaultProjectName },
|
||||
context: { defaultDirectory },
|
||||
},
|
||||
} = useGlobalStateContext()
|
||||
|
||||
async function createAndOpenNewProject() {
|
||||
const projects = await getProjectsInDir(defaultDirectory)
|
||||
const nextIndex = await getNextProjectIndex(defaultProjectName, projects)
|
||||
const name = interpolateProjectNameWithIndex(defaultProjectName, nextIndex)
|
||||
const nextIndex = await getNextProjectIndex(
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
projects
|
||||
)
|
||||
const name = interpolateProjectNameWithIndex(
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
nextIndex
|
||||
)
|
||||
const newFile = await createNewProject(defaultDirectory + '/' + name)
|
||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { Outlet, useRouteLoaderData, useNavigate } from 'react-router-dom'
|
||||
import { Outlet, useNavigate } from 'react-router-dom'
|
||||
import Introduction from './Introduction'
|
||||
import Camera from './Camera'
|
||||
import Sketching from './Sketching'
|
||||
@ -15,7 +15,10 @@ import UserMenu from './UserMenu'
|
||||
import ProjectMenu from './ProjectMenu'
|
||||
import Export from './Export'
|
||||
import FutureWork from './FutureWork'
|
||||
import { IndexLoaderData, paths } from 'Router'
|
||||
import { paths } from 'Router'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
|
||||
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
||||
|
||||
export const onboardingPaths = {
|
||||
INDEX: '/',
|
||||
@ -86,29 +89,23 @@ export const onboardingRoutes = [
|
||||
]
|
||||
|
||||
export function useNextClick(newStatus: string) {
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const {
|
||||
settings: { send },
|
||||
} = useGlobalStateContext()
|
||||
const navigate = useNavigate()
|
||||
const { project } = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
|
||||
return useCallback(() => {
|
||||
send({
|
||||
type: 'Set Onboarding Status',
|
||||
data: { onboardingStatus: newStatus },
|
||||
})
|
||||
navigate(
|
||||
paths.FILE +
|
||||
'/' +
|
||||
encodeURIComponent(project?.path || 'new') +
|
||||
paths.ONBOARDING.INDEX.slice(0, -1) +
|
||||
newStatus
|
||||
)
|
||||
}, [project, newStatus, send, navigate])
|
||||
navigate(filePath + paths.ONBOARDING.INDEX.slice(0, -1) + newStatus)
|
||||
}, [filePath, newStatus, send, navigate])
|
||||
}
|
||||
|
||||
export function useDismiss() {
|
||||
const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const {
|
||||
settings: { send },
|
||||
} = useGlobalStateContext()
|
||||
@ -119,10 +116,8 @@ export function useDismiss() {
|
||||
type: 'Set Onboarding Status',
|
||||
data: { onboardingStatus: 'dismissed' },
|
||||
})
|
||||
navigate(
|
||||
paths.FILE + '/' + encodeURIComponent(routeData?.project?.path || 'new')
|
||||
)
|
||||
}, [send, navigate, routeData])
|
||||
navigate(filePath)
|
||||
}, [send, navigate, filePath])
|
||||
}
|
||||
|
||||
const Onboarding = () => {
|
||||
|
@ -24,11 +24,19 @@ import {
|
||||
} from 'lib/cameraControls'
|
||||
import { UnitSystem } from 'machines/settingsMachine'
|
||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||
import {
|
||||
createNewProject,
|
||||
getNextProjectIndex,
|
||||
getProjectsInDir,
|
||||
interpolateProjectNameWithIndex,
|
||||
} from 'lib/tauriFS'
|
||||
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
|
||||
|
||||
export const Settings = () => {
|
||||
const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const isFileSettings = location.pathname.includes(paths.FILE)
|
||||
const dotDotSlash = useDotDotSlash()
|
||||
useHotkeys('esc', () => navigate(dotDotSlash()))
|
||||
const {
|
||||
@ -63,6 +71,33 @@ export const Settings = () => {
|
||||
}
|
||||
}
|
||||
|
||||
function restartOnboarding() {
|
||||
send({
|
||||
type: 'Set Onboarding Status',
|
||||
data: { onboardingStatus: '' },
|
||||
})
|
||||
|
||||
if (isFileSettings) {
|
||||
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
|
||||
} else {
|
||||
createAndOpenNewProject()
|
||||
}
|
||||
}
|
||||
|
||||
async function createAndOpenNewProject() {
|
||||
const projects = await getProjectsInDir(defaultDirectory)
|
||||
const nextIndex = await getNextProjectIndex(
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
projects
|
||||
)
|
||||
const name = interpolateProjectNameWithIndex(
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
nextIndex
|
||||
)
|
||||
const newFile = await createNewProject(defaultDirectory + '/' + name)
|
||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-40 overflow-auto body-bg">
|
||||
<AppHeader showToolbar={false} project={loaderData?.project}>
|
||||
@ -257,26 +292,18 @@ export const Settings = () => {
|
||||
))}
|
||||
</select>
|
||||
</SettingsSection>
|
||||
{location.pathname.includes(paths.FILE) && (
|
||||
<SettingsSection
|
||||
title="Onboarding"
|
||||
description="Replay the onboarding process"
|
||||
>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() => {
|
||||
send({
|
||||
type: 'Set Onboarding Status',
|
||||
data: { onboardingStatus: '' },
|
||||
})
|
||||
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
|
||||
}}
|
||||
onClick={restartOnboarding}
|
||||
icon={{ icon: faArrowRotateBack }}
|
||||
>
|
||||
Replay Onboarding
|
||||
</ActionButton>
|
||||
</SettingsSection>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,24 +1,27 @@
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
||||
import { parser_wasm } from './lang/abstractSyntaxTree'
|
||||
import { Program } from './lang/abstractSyntaxTreeTypes'
|
||||
import { getNodeFromPath } from './lang/queryAst'
|
||||
import { enginelessExecutor } from './lib/testHelpers'
|
||||
import {
|
||||
parse,
|
||||
Program,
|
||||
_executor,
|
||||
recast,
|
||||
ProgramMemory,
|
||||
Position,
|
||||
PathToNode,
|
||||
Rotation,
|
||||
SourceRange,
|
||||
} from './lang/executor'
|
||||
import { recast } from './lang/recast'
|
||||
} from './lang/wasm'
|
||||
import { getNodeFromPath } from './lang/queryAst'
|
||||
import { enginelessExecutor } from './lib/testHelpers'
|
||||
import { EditorSelection } from '@codemirror/state'
|
||||
import { EngineCommandManager } from './lang/std/engineConnection'
|
||||
import { KCLError } from './lang/errors'
|
||||
import { deferExecution } from 'lib/utils'
|
||||
import { _executor } from './lang/executor'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { engineCommandManager } from './lang/std/engineConnection'
|
||||
import { DefaultPlanes } from './wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
import { initDefaultPlanes } from './hooks/useAppMode'
|
||||
|
||||
export type Selection = {
|
||||
type: 'default' | 'line-end' | 'line-mid'
|
||||
@ -156,14 +159,12 @@ export interface StoreState {
|
||||
code: string
|
||||
setCode: (code: string) => void
|
||||
deferredSetCode: (code: string) => void
|
||||
executeCode: (code?: string) => void
|
||||
executeCode: (code?: string, force?: boolean) => void
|
||||
formatCode: () => void
|
||||
programMemory: ProgramMemory
|
||||
setProgramMemory: (programMemory: ProgramMemory) => void
|
||||
isShiftDown: boolean
|
||||
setIsShiftDown: (isShiftDown: boolean) => void
|
||||
engineCommandManager?: EngineCommandManager
|
||||
setEngineCommandManager: (engineCommandManager: EngineCommandManager) => void
|
||||
mediaStream?: MediaStream
|
||||
setMediaStream: (mediaStream: MediaStream) => void
|
||||
isStreamReady: boolean
|
||||
@ -183,6 +184,10 @@ export interface StoreState {
|
||||
}) => void
|
||||
isExecuting: boolean
|
||||
setIsExecuting: (isExecuting: boolean) => void
|
||||
defaultPlanes: DefaultPlanes | null
|
||||
setDefaultPlanes: (defaultPlanes: DefaultPlanes) => void
|
||||
currentPlane: string | null
|
||||
setCurrentPlane: (currentPlane: string) => void
|
||||
|
||||
showHomeMenu: boolean
|
||||
setHomeShowMenu: (showMenu: boolean) => void
|
||||
@ -222,11 +227,22 @@ export const useStore = create<StoreState>()(
|
||||
editorView.dispatch({ effects: addLineHighlight.of(selection) })
|
||||
}
|
||||
},
|
||||
executeCode: async (code) => {
|
||||
executeCode: async (code, force) => {
|
||||
if (!get().defaultPlanes) {
|
||||
let defaultPlanes = await initDefaultPlanes(
|
||||
engineCommandManager,
|
||||
true
|
||||
)
|
||||
if (!defaultPlanes) return
|
||||
get().setDefaultPlanes(defaultPlanes)
|
||||
}
|
||||
|
||||
const result = await executeCode({
|
||||
code: code || get().code,
|
||||
lastAst: get().ast,
|
||||
engineCommandManager: get().engineCommandManager,
|
||||
engineCommandManager: engineCommandManager,
|
||||
defaultPlanes: get().defaultPlanes!,
|
||||
force,
|
||||
})
|
||||
if (!result.isChange) {
|
||||
return
|
||||
@ -332,13 +348,20 @@ export const useStore = create<StoreState>()(
|
||||
executeAst: async (ast) => {
|
||||
const _ast = ast || get().ast
|
||||
if (!get().isStreamReady) return
|
||||
const engineCommandManager = get().engineCommandManager!
|
||||
if (!engineCommandManager) return
|
||||
if (!get().defaultPlanes) {
|
||||
let defaultPlanes = await initDefaultPlanes(
|
||||
engineCommandManager,
|
||||
true
|
||||
)
|
||||
if (!defaultPlanes) return
|
||||
get().setDefaultPlanes(defaultPlanes)
|
||||
}
|
||||
|
||||
set({ isExecuting: true })
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
ast: _ast,
|
||||
engineCommandManager,
|
||||
defaultPlanes: get().defaultPlanes!,
|
||||
})
|
||||
set({
|
||||
programMemory,
|
||||
@ -350,13 +373,21 @@ export const useStore = create<StoreState>()(
|
||||
executeAstMock: async (ast) => {
|
||||
const _ast = ast || get().ast
|
||||
if (!get().isStreamReady) return
|
||||
const engineCommandManager = get().engineCommandManager!
|
||||
if (!engineCommandManager) return
|
||||
|
||||
if (!get().defaultPlanes) {
|
||||
let defaultPlanes = await initDefaultPlanes(
|
||||
engineCommandManager,
|
||||
true
|
||||
)
|
||||
if (!defaultPlanes) return
|
||||
get().setDefaultPlanes(defaultPlanes)
|
||||
}
|
||||
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
ast: _ast,
|
||||
engineCommandManager,
|
||||
useFakeExecutor: true,
|
||||
defaultPlanes: get().defaultPlanes!,
|
||||
})
|
||||
set({
|
||||
programMemory,
|
||||
@ -371,7 +402,7 @@ export const useStore = create<StoreState>()(
|
||||
{ focusPath, callBack = () => {} } = {}
|
||||
) => {
|
||||
const newCode = recast(ast)
|
||||
const astWithUpdatedSource = parser_wasm(newCode)
|
||||
const astWithUpdatedSource = parse(newCode)
|
||||
callBack(astWithUpdatedSource)
|
||||
|
||||
set({
|
||||
@ -427,7 +458,7 @@ export const useStore = create<StoreState>()(
|
||||
},
|
||||
formatCode: async () => {
|
||||
const code = get().code
|
||||
const ast = parser_wasm(code)
|
||||
const ast = parse(code)
|
||||
const newCode = recast(ast)
|
||||
set({ code: newCode, ast })
|
||||
},
|
||||
@ -435,8 +466,6 @@ export const useStore = create<StoreState>()(
|
||||
setProgramMemory: (programMemory) => set({ programMemory }),
|
||||
isShiftDown: false,
|
||||
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
|
||||
setEngineCommandManager: (engineCommandManager) =>
|
||||
set({ engineCommandManager }),
|
||||
setMediaStream: (mediaStream) => set({ mediaStream }),
|
||||
isStreamReady: false,
|
||||
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
|
||||
@ -454,9 +483,15 @@ export const useStore = create<StoreState>()(
|
||||
fileId: '',
|
||||
setFileId: (fileId) => set({ fileId }),
|
||||
streamDimensions: { streamWidth: 1280, streamHeight: 720 },
|
||||
setStreamDimensions: (streamDimensions) => set({ streamDimensions }),
|
||||
setStreamDimensions: (streamDimensions) => {
|
||||
set({ streamDimensions })
|
||||
},
|
||||
isExecuting: false,
|
||||
setIsExecuting: (isExecuting) => set({ isExecuting }),
|
||||
defaultPlanes: null,
|
||||
setDefaultPlanes: (defaultPlanes) => set({ defaultPlanes }),
|
||||
currentPlane: null,
|
||||
setCurrentPlane: (currentPlane) => set({ currentPlane }),
|
||||
|
||||
// tauri specific app settings
|
||||
defaultDir: {
|
||||
@ -516,10 +551,14 @@ async function executeCode({
|
||||
engineCommandManager,
|
||||
code,
|
||||
lastAst,
|
||||
defaultPlanes,
|
||||
force,
|
||||
}: {
|
||||
code: string
|
||||
lastAst: Program
|
||||
engineCommandManager?: EngineCommandManager
|
||||
engineCommandManager: EngineCommandManager
|
||||
defaultPlanes: DefaultPlanes
|
||||
force?: boolean
|
||||
}): Promise<
|
||||
| {
|
||||
logs: string[]
|
||||
@ -532,14 +571,14 @@ async function executeCode({
|
||||
> {
|
||||
let ast: Program
|
||||
try {
|
||||
ast = parser_wasm(code)
|
||||
ast = parse(code)
|
||||
} catch (e) {
|
||||
let errors: KCLError[] = []
|
||||
let logs: string[] = [JSON.stringify(e)]
|
||||
if (e instanceof KCLError) {
|
||||
errors = [e]
|
||||
logs = []
|
||||
if (e.msg === 'file is empty') engineCommandManager?.endSession()
|
||||
if (e.msg === 'file is empty') engineCommandManager.endSession()
|
||||
}
|
||||
return {
|
||||
isChange: true,
|
||||
@ -562,12 +601,13 @@ async function executeCode({
|
||||
}
|
||||
// Check if the ast we have is equal to the ast in the storage.
|
||||
// If it is, we don't need to update the ast.
|
||||
if (!engineCommandManager || JSON.stringify(ast) === JSON.stringify(lastAst))
|
||||
if (JSON.stringify(ast) === JSON.stringify(lastAst) && !force)
|
||||
return { isChange: false }
|
||||
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
defaultPlanes,
|
||||
})
|
||||
return {
|
||||
ast,
|
||||
@ -581,10 +621,12 @@ async function executeCode({
|
||||
async function executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
defaultPlanes,
|
||||
useFakeExecutor = false,
|
||||
}: {
|
||||
ast: Program
|
||||
engineCommandManager: EngineCommandManager
|
||||
defaultPlanes: DefaultPlanes
|
||||
useFakeExecutor?: boolean
|
||||
}): Promise<{
|
||||
logs: string[]
|
||||
@ -607,7 +649,8 @@ async function executeAst({
|
||||
root: defaultProgramMemory,
|
||||
return: null,
|
||||
},
|
||||
engineCommandManager
|
||||
engineCommandManager,
|
||||
defaultPlanes
|
||||
))
|
||||
|
||||
await engineCommandManager.waitForAllCommands(ast, programMemory)
|
||||
|
@ -6,10 +6,10 @@
|
||||
serial-integration = { max-threads = 4 }
|
||||
|
||||
[profile.default]
|
||||
slow-timeout = { period = "10s", terminate-after = 1 }
|
||||
slow-timeout = { period = "60s", terminate-after = 1 }
|
||||
|
||||
[profile.ci]
|
||||
slow-timeout = { period = "60s", terminate-after = 10 }
|
||||
slow-timeout = { period = "120s", terminate-after = 10 }
|
||||
|
||||
[[profile.default.overrides]]
|
||||
filter = "test(serial_test_)"
|
||||
|
97
src/wasm-lib/Cargo.lock
generated
97
src/wasm-lib/Cargo.lock
generated
@ -71,9 +71,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.5.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
|
||||
checksum = "f6cd65a4b849ace0b7f6daeebcc1a1d111282227ca745458c61dbf670e52a597"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@ -109,9 +109,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "2.1.0"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
|
||||
checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.48.0",
|
||||
@ -440,9 +440,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.4"
|
||||
version = "4.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136"
|
||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -450,9 +450,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.4"
|
||||
version = "4.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56"
|
||||
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -695,6 +695,20 @@ dependencies = [
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-docs"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c357dec14992ba88803535217ed83d6f6cd80efcb8fa8e3f8a30a9b84fadc1c7"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
@ -788,9 +802,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "expectorate"
|
||||
version = "1.0.7"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "710ab6a2d57038a835d66f78d5af3fa5d27c1ec4682f823b9203c48826cb0591"
|
||||
checksum = "de6f19b25bdfa2747ae775f37cd109c31f1272d4e4c83095be0727840aa1d75f"
|
||||
dependencies = [
|
||||
"console",
|
||||
"newline-converter",
|
||||
@ -1376,7 +1390,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.1.31"
|
||||
version = "0.1.33"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@ -1385,7 +1399,7 @@ dependencies = [
|
||||
"clap",
|
||||
"criterion",
|
||||
"dashmap",
|
||||
"derive-docs",
|
||||
"derive-docs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"expectorate",
|
||||
"futures",
|
||||
"itertools 0.11.0",
|
||||
@ -1394,7 +1408,6 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"parse-display",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"schemars",
|
||||
"serde",
|
||||
@ -1408,13 +1421,14 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.2.26"
|
||||
version = "0.2.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2623ee601ce203476229df3f9d3a14664cb43e3f7455e9ac8ed91aacaa6163d"
|
||||
checksum = "539b323537b877fc8dd130362b8f1af9af8051c19208bb8bfd816ab7c330f2bb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1719,7 +1733,7 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
[[package]]
|
||||
name = "openapitor"
|
||||
version = "0.0.9"
|
||||
source = "git+https://github.com/KittyCAD/kittycad.rs?branch=main#61a16059b3eaf8793a2a2e1edbc0d770f284fea3"
|
||||
source = "git+https://github.com/KittyCAD/kittycad.rs?branch=main#fa0345c514fcc9ae6cd74ae35c8e5c2800fec34f"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"anyhow",
|
||||
@ -2203,9 +2217,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.20"
|
||||
version = "0.11.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
|
||||
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
|
||||
dependencies = [
|
||||
"base64 0.21.4",
|
||||
"bytes",
|
||||
@ -2230,6 +2244,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
@ -2879,6 +2894,27 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take_mut"
|
||||
version = "0.2.2"
|
||||
@ -2945,18 +2981,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.48"
|
||||
version = "1.0.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
|
||||
checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.48"
|
||||
version = "1.0.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
|
||||
checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3081,9 +3117,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2"
|
||||
checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
@ -3303,9 +3339,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649"
|
||||
checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@ -3792,6 +3828,15 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
|
@ -11,7 +11,7 @@ crate-type = ["cdylib"]
|
||||
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
|
||||
gloo-utils = "0.2.0"
|
||||
kcl-lib = { path = "kcl" }
|
||||
kittycad = { version = "0.2.25", default-features = false, features = ["js"] }
|
||||
kittycad = { version = "0.2.31", default-features = false, features = ["js"] }
|
||||
serde_json = "1.0.107"
|
||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.87"
|
||||
@ -20,9 +20,9 @@ wasm-bindgen-futures = "0.4.37"
|
||||
[dev-dependencies]
|
||||
anyhow = "1"
|
||||
image = "0.24.7"
|
||||
kittycad = "0.2.25"
|
||||
kittycad = "0.2.31"
|
||||
pretty_assertions = "1.4.0"
|
||||
reqwest = { version = "0.11.20", default-features = false }
|
||||
reqwest = { version = "0.11.22", default-features = false }
|
||||
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
twenty-twenty = "0.6.1"
|
||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
||||
|
@ -19,6 +19,6 @@ serde_tokenstream = "0.2"
|
||||
syn = { version = "2.0.37", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
expectorate = "1.0.7"
|
||||
expectorate = "1.1.0"
|
||||
openapitor = { git = "https://github.com/KittyCAD/kittycad.rs", branch = "main" }
|
||||
pretty_assertions = "1.4.0"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language"
|
||||
version = "0.1.31"
|
||||
version = "0.1.33"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
@ -11,20 +11,20 @@ license = "MIT"
|
||||
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
||||
async-recursion = "1.0.5"
|
||||
async-trait = "0.1.73"
|
||||
clap = { version = "4.4.3", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
||||
clap = { version = "4.4.6", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
||||
dashmap = "5.5.3"
|
||||
#derive-docs = { version = "0.1.4" }
|
||||
derive-docs = { path = "../derive-docs" }
|
||||
kittycad = { version = "0.2.25", default-features = false, features = ["js"] }
|
||||
derive-docs = { version = "0.1.4" }
|
||||
#derive-docs = { path = "../derive-docs" }
|
||||
kittycad = { version = "0.2.31", default-features = false, features = ["js"] }
|
||||
lazy_static = "1.4.0"
|
||||
parse-display = "0.8.2"
|
||||
regex = "1.7.1"
|
||||
schemars = { version = "0.8", features = ["impl_json_schema", "url", "uuid1"] }
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
serde_json = "1.0.107"
|
||||
thiserror = "1.0.48"
|
||||
thiserror = "1.0.49"
|
||||
ts-rs = { version = "7", package = "ts-rs-json-value", features = ["serde-json-impl", "schemars-impl", "uuid-impl"] }
|
||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
||||
winnow = "0.5.15"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
js-sys = { version = "0.3.64" }
|
||||
@ -36,7 +36,7 @@ web-sys = { version = "0.3.64", features = ["console"] }
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
|
||||
futures = { version = "0.3.28" }
|
||||
reqwest = { version = "0.11.20", default-features = false }
|
||||
reqwest = { version = "0.11.22", default-features = false }
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
|
||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||
@ -52,7 +52,7 @@ debug = true
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5.1"
|
||||
expectorate = "1.0.7"
|
||||
expectorate = "1.1.0"
|
||||
itertools = "0.11.0"
|
||||
pretty_assertions = "1.4.0"
|
||||
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
|
@ -1,24 +1,32 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("parse + lex cube", |b| b.iter(|| lex_and_parse(CUBE_PROGRAM)));
|
||||
c.bench_function("parse + lex big kitt", |b| {
|
||||
b.iter(|| lex_and_parse(include_str!("../../tests/executor/inputs/kittycad_svg.kcl")))
|
||||
});
|
||||
c.bench_function("parse + lex pipes_on_pipes", |b| {
|
||||
b.iter(|| lex_and_parse(include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl")))
|
||||
});
|
||||
pub fn bench_lex(c: &mut Criterion) {
|
||||
c.bench_function("lex_cube", |b| b.iter(|| lex(CUBE_PROGRAM)));
|
||||
c.bench_function("lex_big_kitt", |b| b.iter(|| lex(KITT_PROGRAM)));
|
||||
c.bench_function("lex_pipes_on_pipes", |b| b.iter(|| lex(PIPES_PROGRAM)));
|
||||
}
|
||||
|
||||
pub fn bench_lex_parse(c: &mut Criterion) {
|
||||
c.bench_function("parse_lex_cube", |b| b.iter(|| lex_and_parse(CUBE_PROGRAM)));
|
||||
c.bench_function("parse_lex_big_kitt", |b| b.iter(|| lex_and_parse(KITT_PROGRAM)));
|
||||
c.bench_function("parse_lex_pipes_on_pipes", |b| b.iter(|| lex_and_parse(PIPES_PROGRAM)));
|
||||
}
|
||||
|
||||
fn lex(program: &str) {
|
||||
black_box(kcl_lib::token::lexer(program));
|
||||
}
|
||||
|
||||
fn lex_and_parse(program: &str) {
|
||||
let tokens = kcl_lib::tokeniser::lexer(program);
|
||||
let tokens = kcl_lib::token::lexer(program);
|
||||
let parser = kcl_lib::parser::Parser::new(tokens);
|
||||
parser.ast().unwrap();
|
||||
black_box(parser.ast().unwrap());
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_group!(benches, bench_lex, bench_lex_parse);
|
||||
criterion_main!(benches);
|
||||
|
||||
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
|
||||
const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
|
||||
const CUBE_PROGRAM: &str = r#"fn cube = (pos, scale) => {
|
||||
const sg = startSketchAt(pos)
|
||||
|> line([0, scale], %)
|
||||
|
@ -1 +1 @@
|
||||
enum-variant-size-threshold = 24
|
||||
enum-variant-size-threshold = 48
|
||||
|
15
src/wasm-lib/kcl/fuzz/Cargo.lock
generated
15
src/wasm-lib/kcl/fuzz/Cargo.lock
generated
@ -709,7 +709,6 @@ dependencies = [
|
||||
"kittycad",
|
||||
"lazy_static",
|
||||
"parse-display",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"schemars",
|
||||
"serde",
|
||||
@ -723,6 +722,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1878,9 +1878,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649"
|
||||
checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@ -2158,6 +2158,15 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
|
@ -31,6 +31,8 @@ pub async fn modify_ast_for_sketch(
|
||||
program: &mut Program,
|
||||
// The name of the sketch.
|
||||
sketch_name: &str,
|
||||
// The type of plane the sketch is on. `XY` or `XZ`, etc
|
||||
plane: crate::executor::PlaneType,
|
||||
// The ID of the parent sketch.
|
||||
sketch_id: uuid::Uuid,
|
||||
) -> Result<String, KclError> {
|
||||
@ -153,10 +155,11 @@ pub async fn modify_ast_for_sketch(
|
||||
y: (first_control_points.points[1].y - first_control_points.points[0].y),
|
||||
z: (first_control_points.points[1].z - first_control_points.points[0].z),
|
||||
};
|
||||
let sketch = create_start_sketch_at(
|
||||
let sketch = create_start_sketch_on(
|
||||
sketch_name,
|
||||
[first_control_points.points[0].x, first_control_points.points[0].y],
|
||||
[start_sketch_at_end.x, start_sketch_at_end.y],
|
||||
plane,
|
||||
additional_lines,
|
||||
)?;
|
||||
|
||||
@ -166,7 +169,7 @@ pub async fn modify_ast_for_sketch(
|
||||
let recasted = program.recast(&FormatOptions::default(), 0);
|
||||
|
||||
// Re-parse the ast so we get the correct source ranges.
|
||||
let tokens = crate::tokeniser::lexer(&recasted);
|
||||
let tokens = crate::token::lexer(&recasted);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
*program = parser.ast()?;
|
||||
|
||||
@ -174,19 +177,24 @@ pub async fn modify_ast_for_sketch(
|
||||
}
|
||||
|
||||
/// Create a pipe expression that starts a sketch at the given point and draws a line to the given point.
|
||||
fn create_start_sketch_at(
|
||||
fn create_start_sketch_on(
|
||||
name: &str,
|
||||
start: [f64; 2],
|
||||
end: [f64; 2],
|
||||
plane: crate::executor::PlaneType,
|
||||
additional_lines: Vec<[f64; 2]>,
|
||||
) -> Result<VariableDeclarator, KclError> {
|
||||
let start_sketch_at = CallExpression::new(
|
||||
"startSketchAt",
|
||||
vec![ArrayExpression::new(vec![
|
||||
let start_sketch_on = CallExpression::new("startSketchOn", vec![Literal::new(plane.to_string().into()).into()])?;
|
||||
let start_profile_at = CallExpression::new(
|
||||
"startProfileAt",
|
||||
vec![
|
||||
ArrayExpression::new(vec![
|
||||
Literal::new(round_before_recast(start[0]).into()).into(),
|
||||
Literal::new(round_before_recast(start[1]).into()).into(),
|
||||
])
|
||||
.into()],
|
||||
.into(),
|
||||
PipeSubstitution::new().into(),
|
||||
],
|
||||
)?;
|
||||
|
||||
// Keep track of where we are so we can close the sketch if we need to.
|
||||
@ -209,7 +217,7 @@ fn create_start_sketch_at(
|
||||
],
|
||||
)?;
|
||||
|
||||
let mut pipe_body = vec![start_sketch_at.into(), initial_line.into()];
|
||||
let mut pipe_body = vec![start_sketch_on.into(), start_profile_at.into(), initial_line.into()];
|
||||
|
||||
for (index, line) in additional_lines.iter().enumerate() {
|
||||
current_position.x += line[0];
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Data types for the AST.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, fmt::Write};
|
||||
|
||||
use anyhow::Result;
|
||||
use parse_display::{Display, FromStr};
|
||||
@ -10,9 +10,8 @@ use serde_json::Map;
|
||||
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, DocumentSymbol, Range as LspRange, SymbolKind};
|
||||
|
||||
use crate::{
|
||||
engine::EngineConnection,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal},
|
||||
executor::{ExecutorContext, MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal},
|
||||
parser::PIPE_OPERATOR,
|
||||
};
|
||||
|
||||
@ -29,8 +28,8 @@ pub struct Program {
|
||||
impl Program {
|
||||
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||
let indentation = options.get_indentation(indentation_level);
|
||||
let result = self
|
||||
.body
|
||||
let result =
|
||||
self.body
|
||||
.iter()
|
||||
.map(|statement| match statement.clone() {
|
||||
BodyItem::ExpressionStatement(expression_statement) => {
|
||||
@ -41,16 +40,17 @@ impl Program {
|
||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration
|
||||
.declarations
|
||||
.iter()
|
||||
.map(|declaration| {
|
||||
format!(
|
||||
.fold(String::new(), |mut output, declaration| {
|
||||
let _ = write!(
|
||||
output,
|
||||
"{}{} {} = {}",
|
||||
indentation,
|
||||
variable_declaration.kind,
|
||||
declaration.id.name,
|
||||
declaration.init.recast(options, 0, false)
|
||||
)
|
||||
})
|
||||
.collect::<String>(),
|
||||
);
|
||||
output
|
||||
}),
|
||||
BodyItem::ReturnStatement(return_statement) => {
|
||||
format!(
|
||||
"{}return {}",
|
||||
@ -60,7 +60,7 @@ impl Program {
|
||||
}
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(index, recast_str)| {
|
||||
.fold(String::new(), |mut output, (index, recast_str)| {
|
||||
let start_string = if index == 0 {
|
||||
// We need to indent.
|
||||
if let Some(start) = self.non_code_meta.start.clone() {
|
||||
@ -91,9 +91,9 @@ impl Program {
|
||||
custom_white_space_or_comment
|
||||
};
|
||||
|
||||
format!("{}{}{}", start_string, recast_str, end_string)
|
||||
let _ = write!(output, "{}{}{}", start_string, recast_str, end_string);
|
||||
output
|
||||
})
|
||||
.collect::<String>()
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
@ -258,6 +258,23 @@ impl Program {
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace a value with the new value, use the source range for matching the exact value.
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
for item in &mut self.body {
|
||||
match item {
|
||||
BodyItem::ExpressionStatement(ref mut expression_statement) => expression_statement
|
||||
.expression
|
||||
.replace_value(source_range, new_value.clone()),
|
||||
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
|
||||
variable_declaration.replace_value(source_range, new_value.clone())
|
||||
}
|
||||
BodyItem::ReturnStatement(ref mut return_statement) => {
|
||||
return_statement.argument.replace_value(source_range, new_value.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the variable declaration with the given name.
|
||||
pub fn get_variable(&self, name: &str) -> Option<&VariableDeclarator> {
|
||||
for item in &self.body {
|
||||
@ -393,6 +410,27 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
if source_range == self.clone().into() {
|
||||
*self = new_value;
|
||||
return;
|
||||
}
|
||||
|
||||
match self {
|
||||
Value::BinaryExpression(ref mut bin_exp) => bin_exp.replace_value(source_range, new_value),
|
||||
Value::ArrayExpression(ref mut array_exp) => array_exp.replace_value(source_range, new_value),
|
||||
Value::ObjectExpression(ref mut obj_exp) => obj_exp.replace_value(source_range, new_value),
|
||||
Value::MemberExpression(_) => {}
|
||||
Value::Literal(_) => {}
|
||||
Value::FunctionExpression(ref mut func_exp) => func_exp.replace_value(source_range, new_value),
|
||||
Value::CallExpression(ref mut call_exp) => call_exp.replace_value(source_range, new_value),
|
||||
Value::Identifier(_) => {}
|
||||
Value::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value),
|
||||
Value::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
|
||||
Value::PipeSubstitution(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&self) -> usize {
|
||||
match self {
|
||||
Value::Literal(literal) => literal.start(),
|
||||
@ -538,6 +576,23 @@ impl BinaryPart {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
match self {
|
||||
BinaryPart::Literal(_) => {}
|
||||
BinaryPart::Identifier(_) => {}
|
||||
BinaryPart::BinaryExpression(ref mut binary_expression) => {
|
||||
binary_expression.replace_value(source_range, new_value)
|
||||
}
|
||||
BinaryPart::CallExpression(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)
|
||||
}
|
||||
BinaryPart::MemberExpression(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||
match &self {
|
||||
BinaryPart::Literal(literal) => literal.recast(),
|
||||
@ -576,7 +631,7 @@ impl BinaryPart {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
// stop the execution of the pipe.
|
||||
@ -591,13 +646,13 @@ impl BinaryPart {
|
||||
Ok(value.clone())
|
||||
}
|
||||
BinaryPart::BinaryExpression(binary_expression) => {
|
||||
binary_expression.get_result(memory, &mut new_pipe_info, engine).await
|
||||
binary_expression.get_result(memory, &mut new_pipe_info, ctx).await
|
||||
}
|
||||
BinaryPart::CallExpression(call_expression) => {
|
||||
call_expression.execute(memory, &mut new_pipe_info, engine).await
|
||||
call_expression.execute(memory, &mut new_pipe_info, ctx).await
|
||||
}
|
||||
BinaryPart::UnaryExpression(unary_expression) => {
|
||||
unary_expression.get_result(memory, &mut new_pipe_info, engine).await
|
||||
unary_expression.get_result(memory, &mut new_pipe_info, ctx).await
|
||||
}
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(memory),
|
||||
}
|
||||
@ -801,6 +856,12 @@ impl CallExpression {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
for arg in &mut self.arguments {
|
||||
arg.replace_value(source_range, new_value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
|
||||
format!(
|
||||
"{}({})",
|
||||
@ -818,7 +879,7 @@ impl CallExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
let fn_name = self.callee.name.clone();
|
||||
|
||||
@ -832,7 +893,7 @@ impl CallExpression {
|
||||
value.clone()
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
binary_expression.get_result(memory, pipe_info, engine).await?
|
||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
@ -840,15 +901,11 @@ impl CallExpression {
|
||||
// THIS IS IMPORTANT.
|
||||
let mut new_pipe_info = pipe_info.clone();
|
||||
new_pipe_info.is_in_pipe = false;
|
||||
call_expression.execute(memory, &mut new_pipe_info, engine).await?
|
||||
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
unary_expression.get_result(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
object_expression.execute(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
|
||||
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::PipeExpression(pipe_expression) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("PipeExpression not implemented here: {:?}", pipe_expression),
|
||||
@ -880,12 +937,12 @@ impl CallExpression {
|
||||
match &self.function {
|
||||
Function::StdLib { func } => {
|
||||
// Attempt to call the function.
|
||||
let args = crate::std::Args::new(fn_args, self.into(), engine.clone());
|
||||
let args = crate::std::Args::new(fn_args, self.into(), ctx.clone());
|
||||
let result = func.std_lib_fn()(args).await?;
|
||||
if pipe_info.is_in_pipe {
|
||||
pipe_info.index += 1;
|
||||
pipe_info.previous_results.push(result);
|
||||
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine).await
|
||||
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), ctx).await
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
@ -893,7 +950,7 @@ impl CallExpression {
|
||||
Function::InMemory => {
|
||||
let func = memory.get(&fn_name, self.into())?;
|
||||
let result = func
|
||||
.call_fn(fn_args, memory.clone(), engine.clone())
|
||||
.call_fn(fn_args, memory.clone(), ctx.clone())
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
KclError::UndefinedValue(KclErrorDetails {
|
||||
@ -908,7 +965,7 @@ impl CallExpression {
|
||||
pipe_info.index += 1;
|
||||
pipe_info.previous_results.push(result);
|
||||
|
||||
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine).await
|
||||
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), ctx).await
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
@ -1014,6 +1071,12 @@ impl VariableDeclaration {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
for declaration in &mut self.declarations {
|
||||
declaration.init.replace_value(source_range, new_value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a value that includes the given character position.
|
||||
pub fn get_value_for_position(&self, pos: usize) -> Option<&Value> {
|
||||
for declaration in &self.declarations {
|
||||
@ -1367,6 +1430,12 @@ impl ArrayExpression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
for element in &mut self.elements {
|
||||
element.replace_value(source_range, new_value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||
if self.elements.is_empty() {
|
||||
return ConstraintLevel::Ignore {
|
||||
@ -1434,7 +1503,7 @@ impl ArrayExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
let mut results = Vec::with_capacity(self.elements.len());
|
||||
|
||||
@ -1446,7 +1515,7 @@ impl ArrayExpression {
|
||||
value.clone()
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
binary_expression.get_result(memory, pipe_info, engine).await?
|
||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
@ -1454,16 +1523,12 @@ impl ArrayExpression {
|
||||
// THIS IS IMPORTANT.
|
||||
let mut new_pipe_info = pipe_info.clone();
|
||||
new_pipe_info.is_in_pipe = false;
|
||||
call_expression.execute(memory, &mut new_pipe_info, engine).await?
|
||||
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
unary_expression.get_result(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
object_expression.execute(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
|
||||
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine).await?,
|
||||
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::PipeSubstitution(pipe_substitution) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
|
||||
@ -1517,6 +1582,12 @@ impl ObjectExpression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
for property in &mut self.properties {
|
||||
property.value.replace_value(source_range, new_value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||
if self.properties.is_empty() {
|
||||
return ConstraintLevel::Ignore {
|
||||
@ -1584,7 +1655,7 @@ impl ObjectExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
let mut object = Map::new();
|
||||
for property in &self.properties {
|
||||
@ -1595,7 +1666,7 @@ impl ObjectExpression {
|
||||
value.clone()
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
binary_expression.get_result(memory, pipe_info, engine).await?
|
||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
@ -1603,16 +1674,12 @@ impl ObjectExpression {
|
||||
// THIS IS IMPORTANT.
|
||||
let mut new_pipe_info = pipe_info.clone();
|
||||
new_pipe_info.is_in_pipe = false;
|
||||
call_expression.execute(memory, &mut new_pipe_info, engine).await?
|
||||
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
unary_expression.get_result(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
object_expression.execute(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
|
||||
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine).await?,
|
||||
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::PipeSubstitution(pipe_substitution) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
|
||||
@ -1961,6 +2028,11 @@ impl BinaryExpression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
self.left.replace_value(source_range, new_value.clone());
|
||||
self.right.replace_value(source_range, new_value);
|
||||
}
|
||||
|
||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||
let left_constraint_level = self.left.get_constraint_level();
|
||||
let right_constraint_level = self.right.get_constraint_level();
|
||||
@ -2025,7 +2097,7 @@ impl BinaryExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
// stop the execution of the pipe.
|
||||
@ -2035,12 +2107,12 @@ impl BinaryExpression {
|
||||
|
||||
let left_json_value = self
|
||||
.left
|
||||
.get_result(memory, &mut new_pipe_info, engine)
|
||||
.get_result(memory, &mut new_pipe_info, ctx)
|
||||
.await?
|
||||
.get_json_value()?;
|
||||
let right_json_value = self
|
||||
.right
|
||||
.get_result(memory, &mut new_pipe_info, engine)
|
||||
.get_result(memory, &mut new_pipe_info, ctx)
|
||||
.await?
|
||||
.get_json_value()?;
|
||||
|
||||
@ -2183,6 +2255,10 @@ impl UnaryExpression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
self.argument.replace_value(source_range, new_value);
|
||||
}
|
||||
|
||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||
self.argument.get_constraint_level()
|
||||
}
|
||||
@ -2195,7 +2271,7 @@ impl UnaryExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
// stop the execution of the pipe.
|
||||
@ -2206,7 +2282,7 @@ impl UnaryExpression {
|
||||
let num = parse_json_number_as_f64(
|
||||
&self
|
||||
.argument
|
||||
.get_result(memory, &mut new_pipe_info, engine)
|
||||
.get_result(memory, &mut new_pipe_info, ctx)
|
||||
.await?
|
||||
.get_json_value()?,
|
||||
self.into(),
|
||||
@ -2278,6 +2354,12 @@ impl PipeExpression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
for value in &mut self.body {
|
||||
value.replace_value(source_range, new_value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||
if self.body.is_empty() {
|
||||
return ConstraintLevel::Ignore {
|
||||
@ -2333,12 +2415,12 @@ impl PipeExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
// Reset the previous results.
|
||||
pipe_info.previous_results = vec![];
|
||||
pipe_info.index = 0;
|
||||
execute_pipe_body(memory, &self.body, pipe_info, self.into(), engine).await
|
||||
execute_pipe_body(memory, &self.body, pipe_info, self.into(), ctx).await
|
||||
}
|
||||
|
||||
/// Rename all identifiers that have the old name to the new given name.
|
||||
@ -2355,7 +2437,7 @@ async fn execute_pipe_body(
|
||||
body: &[Value],
|
||||
pipe_info: &mut PipeInfo,
|
||||
source_range: SourceRange,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
if pipe_info.index == body.len() {
|
||||
pipe_info.is_in_pipe = false;
|
||||
@ -2380,15 +2462,15 @@ async fn execute_pipe_body(
|
||||
|
||||
match expression {
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
let result = binary_expression.get_result(memory, pipe_info, engine).await?;
|
||||
let result = binary_expression.get_result(memory, pipe_info, ctx).await?;
|
||||
pipe_info.previous_results.push(result);
|
||||
pipe_info.index += 1;
|
||||
execute_pipe_body(memory, body, pipe_info, source_range, engine).await
|
||||
execute_pipe_body(memory, body, pipe_info, source_range, ctx).await
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
pipe_info.is_in_pipe = true;
|
||||
pipe_info.body = body.to_vec();
|
||||
call_expression.execute(memory, pipe_info, engine).await
|
||||
call_expression.execute(memory, pipe_info, ctx).await
|
||||
}
|
||||
_ => {
|
||||
// Return an error this should not happen.
|
||||
@ -2420,6 +2502,10 @@ impl FunctionExpression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||
self.body.replace_value(source_range, new_value);
|
||||
}
|
||||
|
||||
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||
// We don't want to end with a new line inside nested functions.
|
||||
let mut new_options = options.clone();
|
||||
@ -2677,7 +2763,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_get_lsp_symbols() {
|
||||
let code = r#"const part001 = startSketchAt([0.0000000000, 5.0000000000])
|
||||
let code = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0000000000, 5.0000000000], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|
||||
const part002 = "part002"
|
||||
@ -2691,42 +2778,28 @@ fn ghi = (x) => {
|
||||
}
|
||||
|
||||
show(part001)"#;
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let tokens = crate::token::lexer(code);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let symbols = program.get_lsp_symbols(code);
|
||||
assert_eq!(symbols.len(), 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recast_with_std_and_non_stdlib() {
|
||||
let some_program_string = r#"{"body":[{"type":"VariableDeclaration","start":0,"end":0,"declarations":[{"type":"VariableDeclarator","start":0,"end":0,"id":{"type":"Identifier","start":0,"end":0,"name":"part001"},"init":{"type":"PipeExpression","start":0,"end":0,"body":[{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"startSketchAt"},"function":{"type":"StdLib","func":{"name":"startSketchAt","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":"default","raw":"default"}]},{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"ry"},"function":{"type":"InMemory"},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":90,"raw":"90"},{"type":"PipeSubstitution","start":0,"end":0}]},{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"line"},"function":{"type":"StdLib","func":{"name":"line","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":"default","raw":"default"},{"type":"PipeSubstitution","start":0,"end":0}]}],"nonCodeMeta":{"nonCodeNodes":{},"start":null}}}],"kind":"const"},{"type":"ExpressionStatement","start":0,"end":0,"expression":{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"show"},"function":{"type":"StdLib","func":{"name":"show","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Identifier","start":0,"end":0,"name":"part001"}]}}],"start":0,"end":0,"nonCodeMeta":{"nonCodeNodes":{},"start":null}}"#;
|
||||
let some_program: crate::ast::types::Program = serde_json::from_str(some_program_string).unwrap();
|
||||
|
||||
let recasted = some_program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const part001 = startSketchAt('default')
|
||||
|> ry(90, %)
|
||||
|> line('default', %)
|
||||
show(part001)
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recast_with_bad_indentation() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|> line([0.6804562304, 0.9087880491], %)"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|> line([0.6804562304, 0.9087880491], %)
|
||||
"#
|
||||
@ -2735,17 +2808,19 @@ show(part001)
|
||||
|
||||
#[test]
|
||||
fn test_recast_with_bad_indentation_and_inline_comment() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %) // hello world
|
||||
|> line([0.6804562304, 0.9087880491], %)"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %) // hello world
|
||||
|> line([0.6804562304, 0.9087880491], %)
|
||||
"#
|
||||
@ -2753,18 +2828,20 @@ show(part001)
|
||||
}
|
||||
#[test]
|
||||
fn test_recast_with_bad_indentation_and_line_comment() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
// hello world
|
||||
|> line([0.6804562304, 0.9087880491], %)"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
// hello world
|
||||
|> line([0.6804562304, 0.9087880491], %)
|
||||
@ -2783,7 +2860,7 @@ show(part001)
|
||||
// this is also a comment
|
||||
return things
|
||||
}"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -2806,7 +2883,8 @@ show(part001)
|
||||
#[test]
|
||||
fn test_recast_lots_of_comments() {
|
||||
let some_program_string = r#"// comment at start
|
||||
const mySk1 = startSketchAt([0, 0])
|
||||
const mySk1 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
// comment here
|
||||
|> lineTo({ to: [0, 1], tag: 'myTag' }, %)
|
||||
@ -2820,7 +2898,7 @@ const mySk1 = startSketchAt([0, 0])
|
||||
|> ry(45, %)
|
||||
|> rx(45, %)
|
||||
// one more for good measure"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -2828,7 +2906,8 @@ const mySk1 = startSketchAt([0, 0])
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"// comment at start
|
||||
const mySk1 = startSketchAt([0, 0])
|
||||
const mySk1 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
// comment here
|
||||
|> lineTo({ to: [0, 1], tag: 'myTag' }, %)
|
||||
@ -2848,7 +2927,8 @@ a comment between pipe expression statements */
|
||||
|
||||
#[test]
|
||||
fn test_recast_multiline_object() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([-0.01, -0.08])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-0.01, -0.08], %)
|
||||
|> line({ to: [0.62, 4.15], tag: 'seg01' }, %)
|
||||
|> line([2.77, -1.24], %)
|
||||
|> angledLineThatIntersects({
|
||||
@ -2859,7 +2939,7 @@ a comment between pipe expression statements */
|
||||
|> line([-0.42, -1.72], %)
|
||||
|
||||
show(part001)"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -2885,7 +2965,7 @@ const yo = [
|
||||
" hey oooooo really long long long"
|
||||
]
|
||||
"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -2903,7 +2983,7 @@ const key = 'c'
|
||||
const things = "things"
|
||||
|
||||
// this is also a comment"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -2921,7 +3001,7 @@ const things = "things"
|
||||
// a comment
|
||||
"
|
||||
}"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -2936,7 +3016,8 @@ const myVar2 = 5
|
||||
const myVar3 = 6
|
||||
const myAng = 40
|
||||
const myAng2 = 134
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|
||||
|> angledLineToX([
|
||||
-angleToMatchLengthX('seg01', myVar, %),
|
||||
@ -2946,7 +3027,7 @@ const part001 = startSketchAt([0, 0])
|
||||
-angleToMatchLengthY('seg01', myVar, %),
|
||||
myVar
|
||||
], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -2961,7 +3042,8 @@ const myVar2 = 5
|
||||
const myVar3 = 6
|
||||
const myAng = 40
|
||||
const myAng2 = 134
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|
||||
|> angledLineToX([
|
||||
-angleToMatchLengthX('seg01', myVar, %),
|
||||
@ -2972,7 +3054,7 @@ const part001 = startSketchAt([0, 0])
|
||||
myVar
|
||||
], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
|
||||
"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -2989,7 +3071,8 @@ const part001 = startSketchAt([0, 0])
|
||||
|
||||
#[test]
|
||||
fn test_recast_after_rename_std() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([0.0000000000, 5.0000000000])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0000000000, 5.0000000000], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|
||||
const part002 = "part002"
|
||||
@ -3003,7 +3086,7 @@ fn ghi = (part001) => {
|
||||
}
|
||||
|
||||
show(part001)"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let mut program = parser.ast().unwrap();
|
||||
program.rename_symbol("mySuperCoolPart", 6);
|
||||
@ -3011,7 +3094,8 @@ show(part001)"#;
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const mySuperCoolPart = startSketchAt([0.0, 5.0])
|
||||
r#"const mySuperCoolPart = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|
||||
const part002 = "part002"
|
||||
@ -3034,7 +3118,7 @@ show(mySuperCoolPart)
|
||||
let some_program_string = r#"fn ghi = (x, y, z) => {
|
||||
return x
|
||||
}"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let mut program = parser.ast().unwrap();
|
||||
program.rename_symbol("newName", 10);
|
||||
@ -3055,7 +3139,8 @@ show(mySuperCoolPart)
|
||||
const l = 8
|
||||
const h = 10
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -3063,7 +3148,7 @@ const firstExtrude = startSketchAt([0,0])
|
||||
|> extrude(h, %)
|
||||
|
||||
show(firstExtrude)"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -3074,7 +3159,8 @@ show(firstExtrude)"#;
|
||||
const l = 8
|
||||
const h = 10
|
||||
|
||||
const firstExtrude = startSketchAt([0, 0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -3089,7 +3175,7 @@ show(firstExtrude)
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_recast_math_start_negative() {
|
||||
let some_program_string = r#"const myVar = -5 + 6"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
@ -3105,7 +3191,7 @@ const FOS = 2
|
||||
const sigmaAllow = 8
|
||||
const width = 20
|
||||
const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
|
||||
let tokens = crate::tokeniser::lexer(some_program_string);
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
|
||||
|
@ -3,13 +3,15 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use kittycad::types::{Color, ModelingCmd, Point3D};
|
||||
use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
|
||||
|
||||
use crate::{
|
||||
ast::types::{BodyItem, Function, FunctionExpression, Value},
|
||||
engine::EngineConnection,
|
||||
engine::{EngineConnection, EngineManager},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
};
|
||||
|
||||
@ -101,6 +103,7 @@ impl ProgramReturn {
|
||||
#[serde(tag = "type")]
|
||||
pub enum MemoryItem {
|
||||
UserVal(UserVal),
|
||||
Plane(Box<Plane>),
|
||||
SketchGroup(Box<SketchGroup>),
|
||||
ExtrudeGroup(Box<ExtrudeGroup>),
|
||||
#[ts(skip)]
|
||||
@ -115,6 +118,166 @@ pub enum MemoryItem {
|
||||
},
|
||||
}
|
||||
|
||||
/// A plane.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Plane {
|
||||
/// The id of the plane.
|
||||
pub id: uuid::Uuid,
|
||||
// The code for the plane either a string or custom.
|
||||
pub value: PlaneType,
|
||||
/// Origin of the plane.
|
||||
pub origin: Point3d,
|
||||
/// What should the plane’s X axis be?
|
||||
pub x_axis: Point3d,
|
||||
/// What should the plane’s Y axis be?
|
||||
pub y_axis: Point3d,
|
||||
/// The z-axis (normal).
|
||||
pub z_axis: Point3d,
|
||||
#[serde(rename = "__meta")]
|
||||
pub meta: Vec<Metadata>,
|
||||
}
|
||||
|
||||
/// Type for a plane.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[display(style = "camelCase")]
|
||||
pub enum PlaneType {
|
||||
#[serde(rename = "XY", alias = "xy")]
|
||||
#[display("XY")]
|
||||
XY,
|
||||
#[serde(rename = "XZ", alias = "xz")]
|
||||
#[display("XZ")]
|
||||
XZ,
|
||||
#[serde(rename = "YZ", alias = "yz")]
|
||||
#[display("YZ")]
|
||||
YZ,
|
||||
/// A custom plane.
|
||||
#[serde(rename = "Custom")]
|
||||
#[display("Custom")]
|
||||
Custom,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DefaultPlanes {
|
||||
pub xy: uuid::Uuid,
|
||||
pub xz: uuid::Uuid,
|
||||
pub yz: uuid::Uuid,
|
||||
}
|
||||
|
||||
impl DefaultPlanes {
|
||||
pub async fn new(engine: &EngineConnection) -> Result<Self, KclError> {
|
||||
// Create new default planes.
|
||||
let default_size = 60.0;
|
||||
let default_origin = Point3D { x: 0.0, y: 0.0, z: 0.0 };
|
||||
|
||||
// Create xy plane.
|
||||
let xy = uuid::Uuid::new_v4();
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
xy,
|
||||
SourceRange::default(),
|
||||
ModelingCmd::MakePlane {
|
||||
clobber: false,
|
||||
origin: default_origin.clone(),
|
||||
size: default_size,
|
||||
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
||||
hide: Some(true),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
// Set the color.
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
ModelingCmd::PlaneSetColor {
|
||||
color: Color {
|
||||
r: 0.7,
|
||||
g: 0.28,
|
||||
b: 0.28,
|
||||
a: 0.4,
|
||||
},
|
||||
plane_id: xy,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Create yz plane.
|
||||
let yz = uuid::Uuid::new_v4();
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
yz,
|
||||
SourceRange::default(),
|
||||
ModelingCmd::MakePlane {
|
||||
clobber: false,
|
||||
origin: default_origin.clone(),
|
||||
size: default_size,
|
||||
x_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
||||
y_axis: Point3D { x: 0.0, y: 0.0, z: 1.0 },
|
||||
hide: Some(true),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
// Set the color.
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
ModelingCmd::PlaneSetColor {
|
||||
color: Color {
|
||||
r: 0.28,
|
||||
g: 0.7,
|
||||
b: 0.28,
|
||||
a: 0.4,
|
||||
},
|
||||
plane_id: yz,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Create xz plane.
|
||||
let xz = uuid::Uuid::new_v4();
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
xz,
|
||||
SourceRange::default(),
|
||||
ModelingCmd::MakePlane {
|
||||
clobber: false,
|
||||
origin: default_origin,
|
||||
size: default_size,
|
||||
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3D { x: 0.0, y: 0.0, z: 1.0 },
|
||||
hide: Some(true),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
// Set the color.
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
ModelingCmd::PlaneSetColor {
|
||||
color: Color {
|
||||
r: 0.28,
|
||||
g: 0.28,
|
||||
b: 0.7,
|
||||
a: 0.4,
|
||||
},
|
||||
plane_id: xz,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Self { xy, xz, yz })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
@ -140,7 +303,7 @@ pub type MemoryFunction =
|
||||
memory: ProgramMemory,
|
||||
expression: Box<FunctionExpression>,
|
||||
metadata: Vec<Metadata>,
|
||||
engine: EngineConnection,
|
||||
ctx: ExecutorContext,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>;
|
||||
|
||||
fn force_memory_function<
|
||||
@ -149,7 +312,7 @@ fn force_memory_function<
|
||||
ProgramMemory,
|
||||
Box<FunctionExpression>,
|
||||
Vec<Metadata>,
|
||||
EngineConnection,
|
||||
ExecutorContext,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>,
|
||||
>(
|
||||
f: F,
|
||||
@ -165,6 +328,7 @@ impl From<MemoryItem> for Vec<SourceRange> {
|
||||
MemoryItem::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||
MemoryItem::ExtrudeTransform(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||
MemoryItem::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
||||
MemoryItem::Plane(p) => p.meta.iter().map(|m| m.source_range).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,11 +351,11 @@ impl MemoryItem {
|
||||
&self,
|
||||
args: Vec<MemoryItem>,
|
||||
memory: ProgramMemory,
|
||||
engine: EngineConnection,
|
||||
ctx: ExecutorContext,
|
||||
) -> Result<Option<ProgramReturn>, KclError> {
|
||||
if let MemoryItem::Function { func, expression, meta } = &self {
|
||||
if let Some(func) = func {
|
||||
func(args, memory, expression.clone(), meta.clone(), engine).await
|
||||
func(args, memory, expression.clone(), meta.clone(), ctx).await
|
||||
} else {
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Not a function: {:?}", expression),
|
||||
@ -222,6 +386,8 @@ pub struct SketchGroup {
|
||||
pub position: Position,
|
||||
/// The rotation of the sketch group.
|
||||
pub rotation: Rotation,
|
||||
/// The plane id of the sketch group.
|
||||
pub plane_id: Option<uuid::Uuid>,
|
||||
/// Metadata.
|
||||
#[serde(rename = "__meta")]
|
||||
pub meta: Vec<Metadata>,
|
||||
@ -414,6 +580,18 @@ pub struct Point3d {
|
||||
pub z: f64,
|
||||
}
|
||||
|
||||
impl Point3d {
|
||||
pub fn new(x: f64, y: f64, z: f64) -> Self {
|
||||
Self { x, y, z }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point3d> for kittycad::types::Point3D {
|
||||
fn from(p: Point3d) -> Self {
|
||||
Self { x: p.x, y: p.y, z: p.z }
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
@ -593,12 +771,19 @@ impl Default for PipeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// The executor context.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecutorContext {
|
||||
pub engine: EngineConnection,
|
||||
pub planes: DefaultPlanes,
|
||||
}
|
||||
|
||||
/// Execute a AST's program.
|
||||
pub async fn execute(
|
||||
program: crate::ast::types::Program,
|
||||
memory: &mut ProgramMemory,
|
||||
options: BodyType,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<ProgramMemory, KclError> {
|
||||
let mut pipe_info = PipeInfo::default();
|
||||
|
||||
@ -617,7 +802,23 @@ pub async fn execute(
|
||||
args.push(memory_item.clone());
|
||||
}
|
||||
Value::CallExpression(call_expr) => {
|
||||
let result = call_expr.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = call_expr.execute(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
let result = binary_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
let result = unary_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
let result = object_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => {
|
||||
let result = array_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
// We do nothing for the rest.
|
||||
@ -635,7 +836,7 @@ pub async fn execute(
|
||||
|
||||
memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone()));
|
||||
} else if let Some(func) = memory.clone().root.get(&fn_name) {
|
||||
let result = func.call_fn(args.clone(), memory.clone(), engine.clone()).await?;
|
||||
let result = func.call_fn(args.clone(), memory.clone(), ctx.clone()).await?;
|
||||
|
||||
memory.return_ = result;
|
||||
} else {
|
||||
@ -661,7 +862,7 @@ pub async fn execute(
|
||||
memory.add(&var_name, value.clone(), source_range)?;
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
let result = binary_expression.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = binary_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::FunctionExpression(function_expression) => {
|
||||
@ -670,7 +871,7 @@ pub async fn execute(
|
||||
memory: ProgramMemory,
|
||||
function_expression: Box<FunctionExpression>,
|
||||
_metadata: Vec<Metadata>,
|
||||
engine: EngineConnection| {
|
||||
ctx: ExecutorContext| {
|
||||
Box::pin(async move {
|
||||
let mut fn_memory = memory.clone();
|
||||
|
||||
@ -679,7 +880,7 @@ pub async fn execute(
|
||||
message: format!(
|
||||
"Expected {} arguments, got {}",
|
||||
function_expression.params.len(),
|
||||
args.len()
|
||||
args.len(),
|
||||
),
|
||||
source_ranges: vec![(&function_expression).into()],
|
||||
}));
|
||||
@ -698,7 +899,7 @@ pub async fn execute(
|
||||
function_expression.body.clone(),
|
||||
&mut fn_memory,
|
||||
BodyType::Block,
|
||||
&engine,
|
||||
&ctx,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -717,11 +918,11 @@ pub async fn execute(
|
||||
)?;
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
let result = call_expression.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = call_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::PipeExpression(pipe_expression) => {
|
||||
let result = pipe_expression.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = pipe_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::PipeSubstitution(pipe_substitution) => {
|
||||
@ -734,11 +935,11 @@ pub async fn execute(
|
||||
}));
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => {
|
||||
let result = array_expression.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = array_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
let result = object_expression.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = object_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::MemberExpression(member_expression) => {
|
||||
@ -746,7 +947,7 @@ pub async fn execute(
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
let result = unary_expression.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = unary_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
}
|
||||
@ -754,11 +955,11 @@ pub async fn execute(
|
||||
}
|
||||
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
|
||||
Value::BinaryExpression(bin_expr) => {
|
||||
let result = bin_expr.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = bin_expr.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::UnaryExpression(unary_expr) => {
|
||||
let result = unary_expr.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = unary_expr.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::Identifier(identifier) => {
|
||||
@ -769,15 +970,15 @@ pub async fn execute(
|
||||
memory.return_ = Some(ProgramReturn::Value(literal.into()));
|
||||
}
|
||||
Value::ArrayExpression(array_expr) => {
|
||||
let result = array_expr.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = array_expr.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::ObjectExpression(obj_expr) => {
|
||||
let result = obj_expr.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = obj_expr.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::CallExpression(call_expr) => {
|
||||
let result = call_expr.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = call_expr.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::MemberExpression(member_expr) => {
|
||||
@ -785,7 +986,7 @@ pub async fn execute(
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::PipeExpression(pipe_expr) => {
|
||||
let result = pipe_expr.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = pipe_expr.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::PipeSubstitution(_) => {}
|
||||
@ -804,12 +1005,14 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let tokens = crate::token::lexer(code);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast()?;
|
||||
let mut mem: ProgramMemory = Default::default();
|
||||
let engine = EngineConnection::new().await?;
|
||||
let memory = execute(program, &mut mem, BodyType::Root, &engine).await?;
|
||||
let planes = DefaultPlanes::new(&engine).await?;
|
||||
let ctx = ExecutorContext { engine, planes };
|
||||
let memory = execute(program, &mut mem, BodyType::Root, &ctx).await?;
|
||||
|
||||
Ok(memory)
|
||||
}
|
||||
@ -833,7 +1036,8 @@ const newVar = myVar + 1"#;
|
||||
async fn test_execute_angled_line_that_intersects() {
|
||||
let ast_fn = |offset: &str| -> String {
|
||||
format!(
|
||||
r#"const part001 = startSketchAt([0, 0])
|
||||
r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo({{to:[2, 2], tag: "yo"}}, %)
|
||||
|> lineTo([3, 1], %)
|
||||
|> angledLineThatIntersects({{
|
||||
@ -880,7 +1084,8 @@ const yo = 5 + 6
|
||||
|
||||
const abc = 3
|
||||
const identifierGuy = 5
|
||||
const part001 = startSketchAt([-1.2, 4.83])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-1.2, 4.83], %)
|
||||
|> line([2.8, 0], %)
|
||||
|> angledLine([100 + 100, 3.01], %)
|
||||
|> angledLine([abc, 3.02], %)
|
||||
@ -897,7 +1102,8 @@ show(part001)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_with_pipe_substitutions_unary() {
|
||||
let ast = r#"const myVar = 3
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [3, 4], tag: 'seg01' }, %)
|
||||
|> line([
|
||||
min(segLen('seg01', %), myVar),
|
||||
@ -912,7 +1118,8 @@ show(part001)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_with_pipe_substitutions() {
|
||||
let ast = r#"const myVar = 3
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [3, 4], tag: 'seg01' }, %)
|
||||
|> line([
|
||||
min(segLen('seg01', %), myVar),
|
||||
@ -935,7 +1142,8 @@ const halfArmAngle = armAngle / 2
|
||||
const arrExpShouldNotBeIncluded = [1, 2, 3]
|
||||
const objExpShouldNotBeIncluded = { a: 1, b: 2, c: 3 }
|
||||
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> yLineTo(1, %)
|
||||
|> xLine(3.84, %) // selection-range-7ish-before-this
|
||||
|
||||
@ -956,7 +1164,8 @@ fn thing = () => {
|
||||
return -8
|
||||
}
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, thing()], %)
|
||||
@ -978,7 +1187,8 @@ fn thing = (x) => {
|
||||
return -x
|
||||
}
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, thing(8)], %)
|
||||
@ -1000,7 +1210,8 @@ fn thing = (x) => {
|
||||
return [0, -x]
|
||||
}
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line(thing(8), %)
|
||||
@ -1026,7 +1237,8 @@ fn thing = (x) => {
|
||||
return other_thing(x)
|
||||
}
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, thing(8)], %)
|
||||
@ -1041,7 +1253,8 @@ show(firstExtrude)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_with_function_sketch() {
|
||||
let ast = r#"fn box = (h, l, w) => {
|
||||
const myBox = startSketchAt([0,0])
|
||||
const myBox = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -1061,7 +1274,8 @@ show(fnBox)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_member_of_object_with_function_period() {
|
||||
let ast = r#"fn box = (obj) => {
|
||||
let myBox = startSketchAt(obj.start)
|
||||
let myBox = startSketchOn('XY')
|
||||
|> startProfileAt(obj.start, %)
|
||||
|> line([0, obj.l], %)
|
||||
|> line([obj.w, 0], %)
|
||||
|> line([0, -obj.l], %)
|
||||
@ -1081,7 +1295,8 @@ show(thisBox)
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_member_of_object_with_function_brace() {
|
||||
let ast = r#"fn box = (obj) => {
|
||||
let myBox = startSketchAt(obj["start"])
|
||||
let myBox = startSketchOn('XY')
|
||||
|> startProfileAt(obj["start"], %)
|
||||
|> line([0, obj["l"]], %)
|
||||
|> line([obj["w"], 0], %)
|
||||
|> line([0, -obj["l"]], %)
|
||||
@ -1101,7 +1316,8 @@ show(thisBox)
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_member_of_object_with_function_mix_period_brace() {
|
||||
let ast = r#"fn box = (obj) => {
|
||||
let myBox = startSketchAt(obj["start"])
|
||||
let myBox = startSketchOn('XY')
|
||||
|> startProfileAt(obj["start"], %)
|
||||
|> line([0, obj["l"]], %)
|
||||
|> line([obj["w"], 0], %)
|
||||
|> line([10 - obj["w"], -obj.l], %)
|
||||
@ -1122,7 +1338,8 @@ show(thisBox)
|
||||
#[ignore] // ignore til we get loops
|
||||
async fn test_execute_with_function_sketch_loop_objects() {
|
||||
let ast = r#"fn box = (obj) => {
|
||||
let myBox = startSketchAt(obj.start)
|
||||
let myBox = startSketchOn('XY')
|
||||
|> startProfileAt(obj.start, %)
|
||||
|> line([0, obj.l], %)
|
||||
|> line([obj.w, 0], %)
|
||||
|> line([0, -obj.l], %)
|
||||
@ -1144,7 +1361,8 @@ for var in [{start: [0,0], l: 6, w: 10, h: 3}, {start: [-10,-10], l: 3, w: 5, h:
|
||||
#[ignore] // ignore til we get loops
|
||||
async fn test_execute_with_function_sketch_loop_array() {
|
||||
let ast = r#"fn box = (h, l, w, start) => {
|
||||
const myBox = startSketchAt([0,0])
|
||||
const myBox = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -1166,7 +1384,8 @@ for var in [[3, 6, 10, [0,0]], [1.5, 3, 5, [-10,-10]]] {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_member_of_array_with_function() {
|
||||
let ast = r#"fn box = (array) => {
|
||||
let myBox = startSketchAt(array[0])
|
||||
let myBox =startSketchOn('XY')
|
||||
|> startProfileAt(array[0], %)
|
||||
|> line([0, array[1]], %)
|
||||
|> line([array[2], 0], %)
|
||||
|> line([0, -array[1]], %)
|
||||
@ -1240,7 +1459,8 @@ const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
fn thickness = () => { return 0.56 }
|
||||
|
||||
const bracket = startSketchAt([0,0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness()], %)
|
||||
@ -1264,7 +1484,8 @@ const leg2 = 8 // inches
|
||||
const thickness_squared = distance * p * FOS * 6 / sigmaAllow
|
||||
const thickness = 0.56 // inches. App does not support square root function yet
|
||||
|
||||
const bracket = startSketchAt([0,0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
@ -1284,7 +1505,8 @@ const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
const thickness_squared = (distance * p * FOS * 6 / (sigmaAllow - width))
|
||||
const thickness = 0.32 // inches. App does not support square root function yet
|
||||
const bracket = startSketchAt([0,0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
@ -1308,7 +1530,8 @@ const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
const thickness_squared = distance * p * FOS * 6 / (sigmaAllow - width)
|
||||
const thickness = 0.32 // inches. App does not support square root function yet
|
||||
const bracket = startSketchAt([0,0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
|
@ -9,4 +9,4 @@ pub mod math_parser;
|
||||
pub mod parser;
|
||||
pub mod server;
|
||||
pub mod std;
|
||||
pub mod tokeniser;
|
||||
pub mod token;
|
||||
|
@ -10,8 +10,8 @@ use crate::{
|
||||
},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::SourceRange,
|
||||
parser::{is_not_code_token, Parser},
|
||||
tokeniser::{Token, TokenType},
|
||||
parser::Parser,
|
||||
token::{Token, TokenType},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone, ts_rs::TS)]
|
||||
@ -334,7 +334,7 @@ impl ReversePolishNotation {
|
||||
return rpn.parse();
|
||||
}
|
||||
|
||||
if is_not_code_token(current_token) {
|
||||
if !current_token.is_code_token() {
|
||||
let rpn = ReversePolishNotation::new(&self.parser.tokens[1..], &self.previous_postfix, &self.operators);
|
||||
return rpn.parse();
|
||||
}
|
||||
@ -704,7 +704,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_expression() {
|
||||
let tokens = crate::tokeniser::lexer("1 + 2");
|
||||
let tokens = crate::token::lexer("1 + 2");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -731,7 +731,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_expression_add_no_spaces() {
|
||||
let tokens = crate::tokeniser::lexer("1+2");
|
||||
let tokens = crate::token::lexer("1+2");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -758,7 +758,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_expression_sub_no_spaces() {
|
||||
let tokens = crate::tokeniser::lexer("1 -2");
|
||||
let tokens = crate::token::lexer("1 -2");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -785,7 +785,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_expression_plus_followed_by_star() {
|
||||
let tokens = crate::tokeniser::lexer("1 + 2 * 3");
|
||||
let tokens = crate::token::lexer("1 + 2 * 3");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -823,7 +823,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_expression_with_parentheses() {
|
||||
let tokens = crate::tokeniser::lexer("1 * ( 2 + 3 )");
|
||||
let tokens = crate::token::lexer("1 * ( 2 + 3 )");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -861,7 +861,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_expression_parens_in_middle() {
|
||||
let tokens = crate::tokeniser::lexer("1 * ( 2 + 3 ) / 4");
|
||||
let tokens = crate::token::lexer("1 * ( 2 + 3 ) / 4");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -910,7 +910,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_expression_parans_and_predence() {
|
||||
let tokens = crate::tokeniser::lexer("1 + ( 2 + 3 ) / 4");
|
||||
let tokens = crate::token::lexer("1 + ( 2 + 3 ) / 4");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -958,7 +958,7 @@ mod test {
|
||||
}
|
||||
#[test]
|
||||
fn test_parse_expression_nested() {
|
||||
let tokens = crate::tokeniser::lexer("1 * (( 2 + 3 ) / 4 + 5 )");
|
||||
let tokens = crate::token::lexer("1 * (( 2 + 3 ) / 4 + 5 )");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -1017,7 +1017,7 @@ mod test {
|
||||
}
|
||||
#[test]
|
||||
fn test_parse_expression_redundant_braces() {
|
||||
let tokens = crate::tokeniser::lexer("1 * ((( 2 + 3 )))");
|
||||
let tokens = crate::token::lexer("1 * ((( 2 + 3 )))");
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -1055,7 +1055,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_reverse_polish_notation_simple() {
|
||||
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 + 2"), &[], &[]);
|
||||
let parser = ReversePolishNotation::new(&crate::token::lexer("1 + 2"), &[], &[]);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
@ -1084,7 +1084,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_reverse_polish_notation_complex() {
|
||||
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 + 2 * 3"), &[], &[]);
|
||||
let parser = ReversePolishNotation::new(&crate::token::lexer("1 + 2 * 3"), &[], &[]);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
@ -1125,7 +1125,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_reverse_polish_notation_complex_with_parentheses() {
|
||||
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 * ( 2 + 3 )"), &[], &[]);
|
||||
let parser = ReversePolishNotation::new(&crate::token::lexer("1 * ( 2 + 3 )"), &[], &[]);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
@ -1179,7 +1179,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_parse_expression_redundant_braces_around_literal() {
|
||||
let code = "2 + (((3)))";
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let tokens = crate::token::lexer(code);
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse().unwrap();
|
||||
assert_eq!(
|
||||
@ -1274,7 +1274,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_parse_expression_braces_around_lots_of_math() {
|
||||
let code = "(distance * p * FOS * 6 / (sigmaAllow * width))";
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let tokens = crate::token::lexer(code);
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse();
|
||||
assert!(result.is_ok());
|
||||
@ -1283,7 +1283,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_parse_expression_braces_around_internals_lots_of_math() {
|
||||
let code = "distance * p * FOS * 6 / (sigmaAllow * width)";
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let tokens = crate::token::lexer(code);
|
||||
let mut parser = MathParser::new(&tokens);
|
||||
let result = parser.parse();
|
||||
assert!(result.is_ok());
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,7 @@ pub struct Backend {
|
||||
/// The types of tokens the server supports.
|
||||
pub token_types: Vec<SemanticTokenType>,
|
||||
/// Token maps.
|
||||
pub token_map: DashMap<String, Vec<crate::tokeniser::Token>>,
|
||||
pub token_map: DashMap<String, Vec<crate::token::Token>>,
|
||||
/// AST maps.
|
||||
pub ast_map: DashMap<String, crate::ast::types::Program>,
|
||||
/// Current code.
|
||||
@ -56,7 +56,7 @@ impl Backend {
|
||||
// Lets update the tokens.
|
||||
self.current_code_map
|
||||
.insert(params.uri.to_string(), params.text.clone());
|
||||
let tokens = crate::tokeniser::lexer(¶ms.text);
|
||||
let tokens = crate::token::lexer(¶ms.text);
|
||||
self.token_map.insert(params.uri.to_string(), tokens.clone());
|
||||
|
||||
// Update the semantic tokens map.
|
||||
@ -69,9 +69,7 @@ impl Backend {
|
||||
continue;
|
||||
};
|
||||
|
||||
if token.token_type == crate::tokeniser::TokenType::Word
|
||||
&& self.stdlib_completions.contains_key(&token.value)
|
||||
{
|
||||
if token.token_type == crate::token::TokenType::Word && self.stdlib_completions.contains_key(&token.value) {
|
||||
// This is a stdlib function.
|
||||
token_type = SemanticTokenType::FUNCTION;
|
||||
}
|
||||
@ -549,7 +547,7 @@ impl LanguageServer for Backend {
|
||||
// Parse the ast.
|
||||
// I don't know if we need to do this again since it should be updated in the context.
|
||||
// But I figure better safe than sorry since this will write back out to the file.
|
||||
let tokens = crate::tokeniser::lexer(¤t_code);
|
||||
let tokens = crate::token::lexer(¤t_code);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let Ok(ast) = parser.ast() else {
|
||||
return Ok(None);
|
||||
@ -581,7 +579,7 @@ impl LanguageServer for Backend {
|
||||
// Parse the ast.
|
||||
// I don't know if we need to do this again since it should be updated in the context.
|
||||
// But I figure better safe than sorry since this will write back out to the file.
|
||||
let tokens = crate::tokeniser::lexer(¤t_code);
|
||||
let tokens = crate::token::lexer(¤t_code);
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let Ok(mut ast) = parser.ast() else {
|
||||
return Ok(None);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user