Compare commits

..

1 Commits

Author SHA1 Message Date
612b57314d Fix cargo publish by using cargo workspace resolver v2 2024-11-08 15:14:22 -05:00
393 changed files with 5992 additions and 378940 deletions

View File

@ -5,8 +5,6 @@ on:
paths: paths:
- 'src/wasm-lib/**.rs' - 'src/wasm-lib/**.rs'
- 'src/wasm-lib/**.hbs' - 'src/wasm-lib/**.hbs'
- 'src/wasm-lib/**.gen'
- 'src/wasm-lib/**.snap'
- '**/Cargo.toml' - '**/Cargo.toml'
- '**/Cargo.lock' - '**/Cargo.lock'
- '**/rust-toolchain.toml' - '**/rust-toolchain.toml'
@ -17,8 +15,6 @@ on:
paths: paths:
- 'src/wasm-lib/**.rs' - 'src/wasm-lib/**.rs'
- 'src/wasm-lib/**.hbs' - 'src/wasm-lib/**.hbs'
- 'src/wasm-lib/**.gen'
- 'src/wasm-lib/**.snap'
- '**/Cargo.toml' - '**/Cargo.toml'
- '**/Cargo.lock' - '**/Cargo.lock'
- '**/rust-toolchain.toml' - '**/rust-toolchain.toml'

View File

@ -19,7 +19,7 @@ $(XSTATE_TYPEGENS): $(TS_SRC)
yarn xstate typegen 'src/**/*.ts?(x)' yarn xstate typegen 'src/**/*.ts?(x)'
public/wasm_lib_bg.wasm: $(WASM_LIB_FILES) public/wasm_lib_bg.wasm: $(WASM_LIB_FILES)
yarn build:wasm yarn build:wasm-dev
node_modules: package.json yarn.lock node_modules: package.json yarn.lock
yarn install yarn install

View File

@ -110,7 +110,7 @@ Which commands from setup are one off vs need to be run every time?
The following will need to be run when checking out a new commit and guarantees the build is not stale: The following will need to be run when checking out a new commit and guarantees the build is not stale:
```bash ```bash
yarn install yarn install
yarn build:wasm yarn build:wasm-dev # or yarn build:wasm for slower but more production-like build
yarn start # or yarn build:local && yarn serve for slower but more production-like build yarn start # or yarn build:local && yarn serve for slower but more production-like build
``` ```

View File

@ -1,10 +1,10 @@
--- ---
title: "angleToMatchLengthX" title: "angleToMatchLengthX"
excerpt: "Returns the angle to match the given length for x." excerpt: "Compute the angle (in degrees) in o"
layout: manual layout: manual
--- ---
Returns the angle to match the given length for x. Compute the angle (in degrees) in o

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,6 @@ layout: manual
## Table of Contents ## Table of Contents
* [Types](kcl/types) * [Types](kcl/types)
* [Modules](kcl/modules)
* [Known Issues](kcl/KNOWN-ISSUES) * [Known Issues](kcl/KNOWN-ISSUES)
* [`abs`](kcl/abs) * [`abs`](kcl/abs)
* [`acos`](kcl/acos) * [`acos`](kcl/acos)
@ -20,7 +19,6 @@ layout: manual
* [`angledLineToX`](kcl/angledLineToX) * [`angledLineToX`](kcl/angledLineToX)
* [`angledLineToY`](kcl/angledLineToY) * [`angledLineToY`](kcl/angledLineToY)
* [`arc`](kcl/arc) * [`arc`](kcl/arc)
* [`arcTo`](kcl/arcTo)
* [`asin`](kcl/asin) * [`asin`](kcl/asin)
* [`assert`](kcl/assert) * [`assert`](kcl/assert)
* [`assertEqual`](kcl/assertEqual) * [`assertEqual`](kcl/assertEqual)

File diff suppressed because one or more lines are too long

View File

@ -31,7 +31,7 @@ map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
r = 10 // radius r = 10 // radius
fn drawCircle = (id) => { fn drawCircle = (id) => {
return startSketchOn("XY") return startSketchOn("XY")
|> circle({ center: [id * 2 * r, 0], radius: r }, %) |> circle({ center: [id * 2 * r, 0], radius: r }, %)
} }
// Call `drawCircle`, passing in each element of the array. // Call `drawCircle`, passing in each element of the array.
@ -47,7 +47,7 @@ r = 10 // radius
// Call `map`, using an anonymous function instead of a named one. // Call `map`, using an anonymous function instead of a named one.
circles = map([1..3], (id) => { circles = map([1..3], (id) => {
return startSketchOn("XY") return startSketchOn("XY")
|> circle({ center: [id * 2 * r, 0], radius: r }, %) |> circle({ center: [id * 2 * r, 0], radius: r }, %)
}) })
``` ```

View File

@ -1,59 +0,0 @@
---
title: "KCL Modules"
excerpt: "Documentation of modules for the KCL language for the Zoo Modeling App."
layout: manual
---
`KCL` allows splitting code up into multiple files. Each file is somewhat
isolated from other files as a separate module.
When you define a function, you can use `export` before it to make it available
to other modules.
```
// util.kcl
export fn increment = (x) => {
return x + 1
}
```
Other files in the project can now import functions that have been exported.
This makes them available to use in another file.
```
// main.kcl
import increment from "util.kcl"
answer = increment(41)
```
Imported files _must_ be in the same project so that units are uniform across
modules. This means that it must be in the same directory.
Import statements must be at the top-level of a file. It is not allowed to have
an `import` statement inside a function or in the body of an if-else.
Multiple functions can be exported in a file.
```
// util.kcl
export fn increment = (x) => {
return x + 1
}
export fn decrement = (x) => {
return x - 1
}
```
When importing, you can import multiple functions at once.
```
import increment, decrement from "util.kcl"
```
Imported symbols can be renamed for convenience or to avoid name collisions.
```
import increment as inc, decrement as dec from "util.kcl"
```

File diff suppressed because one or more lines are too long

View File

@ -96,24 +96,24 @@ fn cube = (length, center) => {
p3 = [l + x, -l + y] p3 = [l + x, -l + y]
return startSketchAt(p0) return startSketchAt(p0)
|> lineTo(p1, %) |> lineTo(p1, %)
|> lineTo(p2, %) |> lineTo(p2, %)
|> lineTo(p3, %) |> lineTo(p3, %)
|> lineTo(p0, %) |> lineTo(p0, %)
|> close(%) |> close(%)
|> extrude(length, %) |> extrude(length, %)
} }
width = 20 width = 20
fn transform = (i) => { fn transform = (i) => {
return { return {
// Move down each time. // Move down each time.
translate: [0, 0, -i * width], translate: [0, 0, -i * width],
// Make the cube longer, wider and flatter each time. // Make the cube longer, wider and flatter each time.
scale: [pow(1.1, i), pow(1.1, i), pow(0.9, i)], scale: [pow(1.1, i), pow(1.1, i), pow(0.9, i)],
// Turn by 15 degrees each time. // Turn by 15 degrees each time.
rotation: { angle: 15 * i, origin: "local" } rotation: { angle: 15 * i, origin: "local" }
} }
} }
myCubes = cube(width, [100, 0]) myCubes = cube(width, [100, 0])
@ -133,25 +133,25 @@ fn cube = (length, center) => {
p3 = [l + x, -l + y] p3 = [l + x, -l + y]
return startSketchAt(p0) return startSketchAt(p0)
|> lineTo(p1, %) |> lineTo(p1, %)
|> lineTo(p2, %) |> lineTo(p2, %)
|> lineTo(p3, %) |> lineTo(p3, %)
|> lineTo(p0, %) |> lineTo(p0, %)
|> close(%) |> close(%)
|> extrude(length, %) |> extrude(length, %)
} }
width = 20 width = 20
fn transform = (i) => { fn transform = (i) => {
return { return {
translate: [0, 0, -i * width], translate: [0, 0, -i * width],
rotation: { rotation: {
angle: 90 * i, angle: 90 * i,
// Rotate around the overall scene's origin. // Rotate around the overall scene's origin.
origin: "global" origin: "global"
}
} }
} }
}
myCubes = cube(width, [100, 100]) myCubes = cube(width, [100, 100])
|> patternTransform(4, transform, %) |> patternTransform(4, transform, %)
``` ```
@ -168,16 +168,16 @@ t = 0.005 // taper factor [0-1)
fn transform = (replicaId) => { fn transform = (replicaId) => {
scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8)) scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))
return { return {
translate: [0, 0, replicaId * 10], translate: [0, 0, replicaId * 10],
scale: [scale, scale, 0] scale: [scale, scale, 0]
} }
} }
// Each layer is just a pretty thin cylinder. // Each layer is just a pretty thin cylinder.
fn layer = () => { fn layer = () => {
return startSketchOn("XY") return startSketchOn("XY")
// or some other plane idk // or some other plane idk
|> circle({ center: [0, 0], radius: 1 }, %, $tag1) |> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|> extrude(h, %) |> extrude(h, %)
} }
// The vase is 100 layers tall. // The vase is 100 layers tall.
// The 100 layers are replica of each other, with a slight transformation applied to each. // The 100 layers are replica of each other, with a slight transformation applied to each.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -38,8 +38,8 @@ cube = startSketchAt([0, 0])
fn cylinder = (radius, tag) => { fn cylinder = (radius, tag) => {
return startSketchAt([0, 0]) return startSketchAt([0, 0])
|> circle({ radius: radius, center: segEnd(tag) }, %) |> circle({ radius: radius, center: segEnd(tag) }, %)
|> extrude(radius, %) |> extrude(radius, %)
} }
cylinder(1, line1) cylinder(1, line1)

View File

@ -38,11 +38,11 @@ cube = startSketchAt([0, 0])
fn cylinder = (radius, tag) => { fn cylinder = (radius, tag) => {
return startSketchAt([0, 0]) return startSketchAt([0, 0])
|> circle({ |> circle({
radius: radius, radius: radius,
center: segStart(tag) center: segStart(tag)
}, %) }, %)
|> extrude(radius, %) |> extrude(radius, %)
} }
cylinder(1, line1) cylinder(1, line1)

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
---
title: "ArcToData"
excerpt: "Data to draw a three point arc (arcTo)."
layout: manual
---
Data to draw a three point arc (arcTo).
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `end` |`[number, number]`| End point of the arc. A point in 3D space | No |
| `interior` |`[number, number]`| Interior point of the arc. A point in 3D space | No |

View File

@ -24,7 +24,7 @@ Autodesk Filmbox (FBX) format
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `format` |enum: `fbx`| | No | | `type` |enum: `fbx`| | No |
---- ----
@ -40,7 +40,7 @@ Binary glTF 2.0. We refer to this as glTF since that is how our customers refer
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `format` |enum: `gltf`| | No | | `type` |enum: `gltf`| | No |
---- ----
@ -56,7 +56,7 @@ Wavefront OBJ format.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `format` |enum: `obj`| | No | | `type` |enum: `obj`| | No |
| `coords` |[`System`](/docs/kcl/types/System)| Co-ordinate system of input data. Defaults to the [KittyCAD co-ordinate system. | No | | `coords` |[`System`](/docs/kcl/types/System)| Co-ordinate system of input data. Defaults to the [KittyCAD co-ordinate system. | No |
| `units` |[`UnitLength`](/docs/kcl/types/UnitLength)| The units of the input data. This is very important for correct scaling and when calculating physics properties like mass, etc. Defaults to millimeters. | No | | `units` |[`UnitLength`](/docs/kcl/types/UnitLength)| The units of the input data. This is very important for correct scaling and when calculating physics properties like mass, etc. Defaults to millimeters. | No |
@ -74,7 +74,7 @@ The PLY Polygon File Format.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `format` |enum: `ply`| | No | | `type` |enum: `ply`| | No |
| `coords` |[`System`](/docs/kcl/types/System)| Co-ordinate system of input data. Defaults to the [KittyCAD co-ordinate system. | No | | `coords` |[`System`](/docs/kcl/types/System)| Co-ordinate system of input data. Defaults to the [KittyCAD co-ordinate system. | No |
| `units` |[`UnitLength`](/docs/kcl/types/UnitLength)| The units of the input data. This is very important for correct scaling and when calculating physics properties like mass, etc. Defaults to millimeters. | No | | `units` |[`UnitLength`](/docs/kcl/types/UnitLength)| The units of the input data. This is very important for correct scaling and when calculating physics properties like mass, etc. Defaults to millimeters. | No |
@ -92,7 +92,7 @@ SolidWorks part (SLDPRT) format.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `format` |enum: `sldprt`| | No | | `type` |enum: `sldprt`| | No |
---- ----
@ -108,7 +108,7 @@ ISO 10303-21 (STEP) format.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `format` |enum: `step`| | No | | `type` |enum: `step`| | No |
---- ----
@ -124,7 +124,7 @@ ST**ereo**L**ithography format.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `format` |enum: `stl`| | No | | `type` |enum: `stl`| | No |
| `coords` |[`System`](/docs/kcl/types/System)| Co-ordinate system of input data. Defaults to the [KittyCAD co-ordinate system. | No | | `coords` |[`System`](/docs/kcl/types/System)| Co-ordinate system of input data. Defaults to the [KittyCAD co-ordinate system. | No |
| `units` |[`UnitLength`](/docs/kcl/types/UnitLength)| The units of the input data. This is very important for correct scaling and when calculating physics properties like mass, etc. Defaults to millimeters. | No | | `units` |[`UnitLength`](/docs/kcl/types/UnitLength)| The units of the input data. This is very important for correct scaling and when calculating physics properties like mass, etc. Defaults to millimeters. | No |

View File

@ -1,16 +0,0 @@
---
title: "KclNone"
excerpt: "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application)."
layout: manual
---
KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).
**Type:** `object`

View File

@ -23,110 +23,8 @@ Any KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `Uuid`| | No | | `type` |enum: `UserVal`| | No |
| `value` |`string`| | No | | `value` |``| | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `Bool`| | No |
| `value` |`boolean`| | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `Number`| | No |
| `value` |`number`| | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `Int`| | No |
| `value` |`integer`| | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `String`| | No |
| `value` |`string`| | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `Array`| | No |
| `value` |`[` [`KclValue`](/docs/kcl/types/KclValue) `]`| | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `Object`| | No |
| `value` |`object`| | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No | | `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
@ -180,7 +78,7 @@ A plane.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: [`Plane`](/docs/kcl/types/Plane)| | No | | `type` |enum: `Plane`| | No |
| `id` |`string`| The id of the plane. | No | | `id` |`string`| The id of the plane. | No |
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| Any KCL value. | No | | `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| Any KCL value. | No |
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No | | `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
@ -213,38 +111,6 @@ A face.
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No | | `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: [`Sketch`](/docs/kcl/types/Sketch)| | No |
| `value` |[`Sketch`](/docs/kcl/types/Sketch)| Any KCL value. | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `Sketches`| | No |
| `value` |`[` [`Sketch`](/docs/kcl/types/Sketch) `]`| | No |
---- ----
An solid is a collection of extrude surfaces. An solid is a collection of extrude surfaces.
@ -324,23 +190,6 @@ Data for an imported geometry.
---- ----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: [`KclNone`](/docs/kcl/types/KclNone)| | No |
| `value` |[`KclNone`](/docs/kcl/types/KclNone)| Any KCL value. | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----

View File

@ -1,27 +0,0 @@
---
title: "Plane"
excerpt: "A plane."
layout: manual
---
A plane.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `id` |`string`| The id of the plane. | No |
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A plane. | No |
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the planes X axis be? | No |
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the planes Y axis be? | No |
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |

View File

@ -1,10 +1,10 @@
--- ---
title: "PlaneData" title: "PlaneData"
excerpt: "Orientation data that can be used to construct a plane, not a plane in itself." excerpt: "Data for a plane."
layout: manual layout: manual
--- ---
Orientation data that can be used to construct a plane, not a plane in itself. Data for a plane.

View File

@ -22,18 +22,6 @@ Data for start sketch on. You can start a sketch on a plane or an solid.
----
Data for start sketch on. You can start a sketch on a plane or an solid.
[`Plane`](/docs/kcl/types/Plane)
---- ----
Data for start sketch on. You can start a sketch on a plane or an solid. Data for start sketch on. You can start a sketch on a plane or an solid.

View File

@ -67,15 +67,15 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
if (openPanes.includes('code')) { if (openPanes.includes('code')) {
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ') await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %)`) |> line([${commonPoints.num1}, 0], %)`)
} }
await page.waitForTimeout(500) await page.waitForTimeout(500)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20) await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
if (openPanes.includes('code')) { if (openPanes.includes('code')) {
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ') await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %) |> line([${commonPoints.num1}, 0], %)
|> yLine(${commonPoints.num1 + 0.01}, %)`) |> line([0, ${commonPoints.num1 + 0.01}], %)`)
} else { } else {
await page.waitForTimeout(500) await page.waitForTimeout(500)
} }
@ -84,9 +84,9 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
if (openPanes.includes('code')) { if (openPanes.includes('code')) {
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ') await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %) |> line([${commonPoints.num1}, 0], %)
|> yLine(${commonPoints.num1 + 0.01}, %) |> line([0, ${commonPoints.num1 + 0.01}], %)
|> xLine(${commonPoints.num2 * -1}, %)`) |> lineTo([0, ${commonPoints.num3}], %)`)
} }
// deselect line tool // deselect line tool
@ -142,9 +142,9 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
await u.openKclCodePanel() await u.openKclCodePanel()
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ') await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %, $seg01) |> line([${commonPoints.num1}, 0], %, $seg01)
|> yLine(${commonPoints.num1 + 0.01}, %) |> line([0, ${commonPoints.num1 + 0.01}], %)
|> xLine(-segLen(seg01), %)`) |> angledLine([180, segLen(seg01)], %)`)
} }
test.describe('Basic sketch', () => { test.describe('Basic sketch', () => {

View File

@ -62,8 +62,6 @@ test(
const errorToastMessage = page.getByText(`Error while exporting`) const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`) const engineErrorToastMessage = page.getByText(`Nothing to export`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`) const alreadyExportingToastMessage = page.getByText(`Already exporting`)
// The open file's name is `main.kcl`, so the export file name should be `main.gltf`
const exportFileName = `main.gltf`
// Click the export button // Click the export button
await exportButton.click() await exportButton.click()
@ -98,7 +96,7 @@ test(
.poll( .poll(
async () => { async () => {
try { try {
const outputGltf = await fsp.readFile(exportFileName) const outputGltf = await fsp.readFile('output.gltf')
return outputGltf.byteLength return outputGltf.byteLength
} catch (e) { } catch (e) {
return 0 return 0
@ -108,8 +106,8 @@ test(
) )
.toBeGreaterThan(300_000) .toBeGreaterThan(300_000)
// clean up exported file // clean up output.gltf
await fsp.rm(exportFileName) await fsp.rm('output.gltf')
}) })
}) })
@ -140,8 +138,6 @@ test(
const errorToastMessage = page.getByText(`Error while exporting`) const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`) const engineErrorToastMessage = page.getByText(`Nothing to export`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`) const alreadyExportingToastMessage = page.getByText(`Already exporting`)
// The open file's name is `other.kcl`, so the export file name should be `other.gltf`
const exportFileName = `other.gltf`
// Click the export button // Click the export button
await exportButton.click() await exportButton.click()
@ -175,7 +171,7 @@ test(
.poll( .poll(
async () => { async () => {
try { try {
const outputGltf = await fsp.readFile(exportFileName) const outputGltf = await fsp.readFile('output.gltf')
return outputGltf.byteLength return outputGltf.byteLength
} catch (e) { } catch (e) {
return 0 return 0
@ -185,8 +181,8 @@ test(
) )
.toBeGreaterThan(100_000) .toBeGreaterThan(100_000)
// clean up exported file // clean up output.gltf
await fsp.rm(exportFileName) await fsp.rm('output.gltf')
}) })
await electronApp.close() await electronApp.close()
}) })

View File

@ -694,9 +694,6 @@ test.describe('Editor tests', () => {
.toHaveText(`sketch001 = startSketchOn('XZ') .toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt([3.14, 12], %) |> startProfileAt([3.14, 12], %)
|> xLine(5, %) // lin`) |> xLine(5, %) // lin`)
// expect there to be no KCL errors
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
}) })
test('with tab to accept the completion', async ({ page }) => { test('with tab to accept the completion', async ({ page }) => {

View File

@ -1135,189 +1135,3 @@ _test.describe('Deleting items from the file pane', () => {
} }
) )
}) })
_test.describe(
'Undo and redo do not keep history when navigating between files',
() => {
_test(
`open a file, change something, open a different file, hitting undo should do nothing`,
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const { page } = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const testDir = join(dir, 'testProject')
await fsp.mkdir(testDir, { recursive: true })
await fsp.copyFile(
executorInputPath('cylinder.kcl'),
join(testDir, 'main.kcl')
)
await fsp.copyFile(
executorInputPath('basic_fillet_cube_end.kcl'),
join(testDir, 'other.kcl')
)
},
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
page.on('console', console.log)
// Constants and locators
const projectCard = page.getByText('testProject')
const otherFile = page
.getByRole('listitem')
.filter({ has: page.getByRole('button', { name: 'other.kcl' }) })
await _test.step(
'Open project and make a change to the file',
async () => {
await projectCard.click()
await u.waitForPageLoad()
// Get the text in the code locator.
const originalText = await u.codeLocator.innerText()
// Click in the editor and add some new lines.
await u.codeLocator.click()
await page.keyboard.type(`sketch001 = startSketchOn('XY')
some other shit`)
// Ensure the content in the editor changed.
const newContent = await u.codeLocator.innerText()
expect(originalText !== newContent)
}
)
await _test.step('navigate to other.kcl', async () => {
await u.openFilePanel()
await otherFile.click()
await u.waitForPageLoad()
await u.openKclCodePanel()
await _expect(u.codeLocator).toContainText('getOppositeEdge(thing)')
})
await _test.step('hit undo', async () => {
// Get the original content of the file.
const originalText = await u.codeLocator.innerText()
// Now hit undo
await page.keyboard.down('ControlOrMeta')
await page.keyboard.press('KeyZ')
await page.keyboard.up('ControlOrMeta')
await page.waitForTimeout(100)
await expect(u.codeLocator).toContainText(originalText)
})
}
)
_test(
`open a file, change something, undo it, open a different file, hitting redo should do nothing`,
{ tag: '@electron' },
// Skip on windows i think the keybindings are different for redo.
async ({ browserName }, testInfo) => {
test.skip(process.platform === 'win32', 'Skip on windows')
const { page } = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const testDir = join(dir, 'testProject')
await fsp.mkdir(testDir, { recursive: true })
await fsp.copyFile(
executorInputPath('cylinder.kcl'),
join(testDir, 'main.kcl')
)
await fsp.copyFile(
executorInputPath('basic_fillet_cube_end.kcl'),
join(testDir, 'other.kcl')
)
},
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
page.on('console', console.log)
// Constants and locators
const projectCard = page.getByText('testProject')
const otherFile = page
.getByRole('listitem')
.filter({ has: page.getByRole('button', { name: 'other.kcl' }) })
const badContent = 'this shit'
await _test.step(
'Open project and make a change to the file',
async () => {
await projectCard.click()
await u.waitForPageLoad()
// Get the text in the code locator.
const originalText = await u.codeLocator.innerText()
// Click in the editor and add some new lines.
await u.codeLocator.click()
await page.keyboard.type(badContent)
// Ensure the content in the editor changed.
const newContent = await u.codeLocator.innerText()
expect(originalText !== newContent)
// Now hit undo
await page.keyboard.down('ControlOrMeta')
await page.keyboard.press('KeyZ')
await page.keyboard.up('ControlOrMeta')
await page.waitForTimeout(100)
await expect(u.codeLocator).toContainText(originalText)
await expect(u.codeLocator).not.toContainText(badContent)
// Hit redo.
await page.keyboard.down('Shift')
await page.keyboard.down('ControlOrMeta')
await page.keyboard.press('KeyZ')
await page.keyboard.up('ControlOrMeta')
await page.keyboard.up('Shift')
await page.waitForTimeout(100)
await expect(u.codeLocator).toContainText(originalText)
await expect(u.codeLocator).toContainText(badContent)
// Now hit undo
await page.keyboard.down('ControlOrMeta')
await page.keyboard.press('KeyZ')
await page.keyboard.up('ControlOrMeta')
await page.waitForTimeout(100)
await expect(u.codeLocator).toContainText(originalText)
await expect(u.codeLocator).not.toContainText(badContent)
}
)
await _test.step('navigate to other.kcl', async () => {
await u.openFilePanel()
await otherFile.click()
await u.waitForPageLoad()
await u.openKclCodePanel()
await _expect(u.codeLocator).toContainText('getOppositeEdge(thing)')
await expect(u.codeLocator).not.toContainText(badContent)
})
await _test.step('hit redo', async () => {
// Get the original content of the file.
const originalText = await u.codeLocator.innerText()
// Now hit redo
await page.keyboard.down('Shift')
await page.keyboard.down('ControlOrMeta')
await page.keyboard.press('KeyZ')
await page.keyboard.up('ControlOrMeta')
await page.keyboard.up('Shift')
await page.waitForTimeout(100)
await expect(u.codeLocator).toContainText(originalText)
await expect(u.codeLocator).not.toContainText(badContent)
})
}
)
}
)

View File

@ -452,7 +452,7 @@ sketch002 = startSketchOn(extrude001, seg03)
) )
}) })
test(`Verify axis, origin, and horizontal snapping`, async ({ test(`Verify axis and origin snapping`, async ({
app, app,
editor, editor,
toolbar, toolbar,
@ -505,7 +505,7 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
const expectedCodeSnippets = { const expectedCodeSnippets = {
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`, sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], %)`, pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], %)`,
segmentOnXAxis: `xLine(${xAxisSloppy.kcl[0]}, %)`, segmentOnXAxis: `lineTo([${xAxisSloppy.kcl[0]}, ${xAxisSloppy.kcl[1]}], %)`,
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], %)`, afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], %)`,
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`, afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
} }

View File

@ -247,7 +247,7 @@ test.describe('Can export from electron app', () => {
.poll( .poll(
async () => { async () => {
try { try {
const outputGltf = await fsp.readFile('main.gltf') const outputGltf = await fsp.readFile('output.gltf')
return outputGltf.byteLength return outputGltf.byteLength
} catch (e) { } catch (e) {
return 0 return 0
@ -257,8 +257,8 @@ test.describe('Can export from electron app', () => {
) )
.toBeGreaterThan(300_000) .toBeGreaterThan(300_000)
// clean up exported file // clean up output.gltf
await fsp.rm('main.gltf') await fsp.rm('output.gltf')
}) })
await electronApp.close() await electronApp.close()

View File

@ -115,7 +115,7 @@ test.describe('Sketch tests', () => {
'persistCode', 'persistCode',
`sketch001 = startSketchOn('XZ') `sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %) |> startProfileAt([4.61, -14.01], %)
|> xLine(12.73, %) |> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)` |> tangentialArcTo([24.95, -5.38], %)`
) )
}) })
@ -156,7 +156,7 @@ test.describe('Sketch tests', () => {
await expect.poll(u.normalisedEditorCode, { timeout: 1000 }) await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
.toBe(`sketch001 = startSketchOn('XZ') .toBe(`sketch001 = startSketchOn('XZ')
|> startProfileAt([12.34, -12.34], %) |> startProfileAt([12.34, -12.34], %)
|> yLine(12.34, %) |> line([-12.34, 12.34], %)
`) `)
}).toPass({ timeout: 40_000, intervals: [1_000] }) }).toPass({ timeout: 40_000, intervals: [1_000] })
@ -645,7 +645,7 @@ test.describe('Sketch tests', () => {
await u.openDebugPanel() await u.openDebugPanel()
const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 } const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 }
const { toSU, toU, click00r } = getMovementUtils({ center, page }) const { toSU, click00r } = getMovementUtils({ center, page })
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
@ -674,15 +674,16 @@ test.describe('Sketch tests', () => {
await click00r(50, 0) await click00r(50, 0)
await page.waitForTimeout(100) await page.waitForTimeout(100)
codeStr += ` |> xLine(${toU(50, 0)[0]}, %)` codeStr += ` |> lineTo(${toSU([50, 0])}, %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
await click00r(0, 50) await click00r(0, 50)
codeStr += ` |> yLine(${toU(0, 50)[1]}, %)` codeStr += ` |> line(${toSU([0, 50])}, %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
await click00r(-50, 0) let clickCoords = await click00r(-50, 0)
codeStr += ` |> xLine(${toU(-50, 0)[0]}, %)` expect(clickCoords).not.toBeUndefined()
codeStr += ` |> lineTo(${toSU(clickCoords!)}, %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
// exit the sketch, reset relative clicker // exit the sketch, reset relative clicker
@ -711,15 +712,15 @@ test.describe('Sketch tests', () => {
// TODO: I couldn't use `toSU` here because of some rounding error causing // TODO: I couldn't use `toSU` here because of some rounding error causing
// it to be off by 0.01 // it to be off by 0.01
await click00r(30, 0) await click00r(30, 0)
codeStr += ` |> xLine(2.04, %)` codeStr += ` |> lineTo([4.07, 0], %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
await click00r(0, 30) await click00r(0, 30)
codeStr += ` |> yLine(-2.03, %)` codeStr += ` |> line([0, -2.03], %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
await click00r(-30, 0) await click00r(-30, 0)
codeStr += ` |> xLine(-2.04, %)` codeStr += ` |> line([-2.04, 0], %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
await click00r(undefined, undefined) await click00r(undefined, undefined)
@ -743,8 +744,8 @@ test.describe('Sketch tests', () => {
const code = `sketch001 = startSketchOn('-XZ') const code = `sketch001 = startSketchOn('-XZ')
|> startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(scale * 34.8)}], %) |> startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(scale * 34.8)}], %)
|> xLine(${roundOff(scale * 139.19)}, %) |> line([${roundOff(scale * 139.19)}, 0], %)
|> yLine(-${roundOff(scale * 139.2)}, %) |> line([0, -${roundOff(scale * 139.2)}], %)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)` |> close(%)`
@ -1274,44 +1275,3 @@ test2.describe('Sketch mode should be toleratant to syntax errors', () => {
} }
) )
}) })
test2.describe(`Sketching with offset planes`, () => {
test2(
`Can select an offset plane to sketch on`,
async ({ app, scene, toolbar, editor }) => {
// We seed the scene with a single offset plane
await app.initialise(`offsetPlane001 = offsetPlane("XY", 10)`)
const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200)
await test2.step(`Start sketching on the offset plane`, async () => {
await toolbar.startSketchPlaneSelection()
await test2.step(`Hovering should highlight code`, async () => {
await planeHover()
await editor.expectState({
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
diagnostics: [],
highlightedCode: 'offsetPlane("XY", 10)',
})
})
await test2.step(
`Clicking should select the plane and enter sketch mode`,
async () => {
await planeClick()
// Have to wait for engine-side animation to finish
await app.page.waitForTimeout(600)
await expect2(toolbar.lineBtn).toBeEnabled()
await editor.expectEditor.toContain('startSketchOn(offsetPlane001)')
await editor.expectState({
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
diagnostics: [],
highlightedCode: '',
})
}
)
})
}
)
})

View File

@ -283,7 +283,7 @@ part001 = startSketchOn('-XZ')
const gltfFilename = filenames.filter((t: string) => const gltfFilename = filenames.filter((t: string) =>
t.includes('.gltf') t.includes('.gltf')
)[0] )[0]
if (!gltfFilename) throw new Error('No gLTF in this archive') if (!gltfFilename) throw new Error('No output.gltf in this archive')
cliCommand = `export ZOO_TOKEN=${secrets.snapshottoken} && zoo file snapshot --output-format=png --src-format=${outputType} ${parentPath}/${gltfFilename} ${imagePath}` cliCommand = `export ZOO_TOKEN=${secrets.snapshottoken} && zoo file snapshot --output-format=png --src-format=${outputType} ${parentPath}/${gltfFilename} ${imagePath}`
} }
@ -462,7 +462,7 @@ test(
await page.waitForTimeout(100) await page.waitForTimeout(100)
code += ` code += `
|> xLine(7.25, %)` |> line([7.25, 0], %)`
await expect(page.locator('.cm-content')).toHaveText(code) await expect(page.locator('.cm-content')).toHaveText(code)
await page await page
@ -647,7 +647,7 @@ test.describe(
await page.waitForTimeout(100) await page.waitForTimeout(100)
code += ` code += `
|> xLine(7.25, %)` |> line([7.25, 0], %)`
await expect(u.codeLocator).toHaveText(code) await expect(u.codeLocator).toHaveText(code)
await page await page
@ -752,7 +752,7 @@ test.describe(
await page.waitForTimeout(100) await page.waitForTimeout(100)
code += ` code += `
|> xLine(184.3, %)` |> line([184.3, 0], %)`
await expect(u.codeLocator).toHaveText(code) await expect(u.codeLocator).toHaveText(code)
await page await page

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -141,7 +141,7 @@ test.describe('Test network and connection issues', () => {
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`sketch001 = startSketchOn('XZ') .toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %)`) |> line([${commonPoints.num1}, 0], %)`)
// Expect the network to be up // Expect the network to be up
await expect(networkToggle).toContainText('Connected') await expect(networkToggle).toContainText('Connected')
@ -207,7 +207,7 @@ test.describe('Test network and connection issues', () => {
await expect.poll(u.normalisedEditorCode) await expect.poll(u.normalisedEditorCode)
.toBe(`sketch001 = startSketchOn('XZ') .toBe(`sketch001 = startSketchOn('XZ')
|> startProfileAt([12.34, -12.34], %) |> startProfileAt([12.34, -12.34], %)
|> xLine(12.34, %) |> line([12.34, 0], %)
|> line([-12.34, 12.34], %) |> line([-12.34, 12.34], %)
`) `)
@ -217,9 +217,9 @@ test.describe('Test network and connection issues', () => {
await expect.poll(u.normalisedEditorCode) await expect.poll(u.normalisedEditorCode)
.toBe(`sketch001 = startSketchOn('XZ') .toBe(`sketch001 = startSketchOn('XZ')
|> startProfileAt([12.34, -12.34], %) |> startProfileAt([12.34, -12.34], %)
|> xLine(12.34, %) |> line([12.34, 0], %)
|> line([-12.34, 12.34], %) |> line([-12.34, 12.34], %)
|> xLine(-12.34, %) |> lineTo([0, -12.34], %)
`) `)

View File

@ -305,7 +305,7 @@ export const getMovementUtils = (opts: any) => {
return [last.x, last.y] return [last.x, last.y]
} }
return { toSU, toU, click00r } return { toSU, click00r }
} }
async function waitForAuthAndLsp(page: Page) { async function waitForAuthAndLsp(page: Page) {

View File

@ -32,17 +32,10 @@ test.describe('Testing selections', () => {
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openDebugPanel() await u.openDebugPanel()
const yAxisClick = () => const xAxisClick = () =>
test.step('Click on Y axis', async () => { page.mouse.click(700, 253).then(() => page.waitForTimeout(100))
await page.mouse.move(600, 200, { steps: 5 })
await page.mouse.click(600, 200)
await page.waitForTimeout(100)
})
const xAxisClickAfterExitingSketch = () => const xAxisClickAfterExitingSketch = () =>
test.step(`Click on X axis after exiting sketch, which shifts it at the moment`, async () => { page.mouse.click(639, 278).then(() => page.waitForTimeout(100))
await page.mouse.click(639, 278)
await page.waitForTimeout(100)
})
const emptySpaceHover = () => const emptySpaceHover = () =>
test.step('Hover over empty space', async () => { test.step('Hover over empty space', async () => {
await page.mouse.move(700, 143, { steps: 5 }) await page.mouse.move(700, 143, { steps: 5 })
@ -87,23 +80,23 @@ test.describe('Testing selections', () => {
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`sketch001 = startSketchOn('XZ') .toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %)`) |> line([${commonPoints.num1}, 0], %)`)
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20) await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`sketch001 = startSketchOn('XZ') .toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %) |> line([${commonPoints.num1}, 0], %)
|> yLine(${commonPoints.num1 + 0.01}, %)`) |> line([0, ${commonPoints.num1 + 0.01}], %)`)
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.mouse.click(startXPx, 500 - PUR * 20) await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`sketch001 = startSketchOn('XZ') .toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %) |> line([${commonPoints.num1}, 0], %)
|> yLine(${commonPoints.num1 + 0.01}, %) |> line([0, ${commonPoints.num1 + 0.01}], %)
|> xLine(${commonPoints.num2 * -1}, %)`) |> lineTo([0, ${commonPoints.num3}], %)`)
// deselect line tool // deselect line tool
await page.getByRole('button', { name: 'line Line', exact: true }).click() await page.getByRole('button', { name: 'line Line', exact: true }).click()
@ -128,58 +121,53 @@ test.describe('Testing selections', () => {
// now check clicking works including axis // now check clicking works including axis
// click a segment hold shift and click an axis, see that a relevant constraint is enabled // click a segment hold shift and click an axis, see that a relevant constraint is enabled
await topHorzSegmentClick()
await page.keyboard.down('Shift')
const constrainButton = page.getByRole('button', { const constrainButton = page.getByRole('button', {
name: 'Length: open menu', name: 'Length: open menu',
}) })
const absXButton = page.getByRole('button', { name: 'Absolute X' }) const absYButton = page.getByRole('button', { name: 'Absolute Y' })
await constrainButton.click()
await test.step(`Select a segment and an axis, see that a relevant constraint is enabled`, async () => { await expect(absYButton).toBeDisabled()
await topHorzSegmentClick() await page.waitForTimeout(100)
await page.keyboard.down('Shift') await xAxisClick()
await constrainButton.click() await page.keyboard.up('Shift')
await expect(absXButton).toBeDisabled() await constrainButton.click()
await page.waitForTimeout(100) await absYButton.and(page.locator(':not([disabled])')).waitFor()
await yAxisClick() await expect(absYButton).not.toBeDisabled()
await page.keyboard.up('Shift')
await constrainButton.click()
await absXButton.and(page.locator(':not([disabled])')).waitFor()
await expect(absXButton).not.toBeDisabled()
})
// clear selection by clicking on nothing
await emptySpaceClick() await emptySpaceClick()
await page.waitForTimeout(100)
// same selection but click the axis first
await xAxisClick()
await constrainButton.click()
await expect(absYButton).toBeDisabled()
await page.keyboard.down('Shift')
await page.waitForTimeout(100)
await topHorzSegmentClick()
await page.waitForTimeout(100) await page.waitForTimeout(100)
await test.step(`Same selection but click the axis first`, async () => { await page.keyboard.up('Shift')
await yAxisClick() await constrainButton.click()
await constrainButton.click() await expect(absYButton).not.toBeDisabled()
await expect(absXButton).toBeDisabled()
await page.keyboard.down('Shift')
await page.waitForTimeout(100)
await topHorzSegmentClick()
await page.waitForTimeout(100)
await page.keyboard.up('Shift')
await constrainButton.click()
await expect(absXButton).not.toBeDisabled()
})
// clear selection by clicking on nothing // clear selection by clicking on nothing
await emptySpaceClick() await emptySpaceClick()
// check the same selection again by putting cursor in code first then selecting axis // check the same selection again by putting cursor in code first then selecting axis
await test.step(`Same selection but code selection then axis`, async () => { await page
await page .getByText(` |> lineTo([0, ${commonPoints.num3}], %)`)
.getByText(` |> xLine(${commonPoints.num2 * -1}, %)`) .click()
.click() await page.keyboard.down('Shift')
await page.keyboard.down('Shift') await constrainButton.click()
await constrainButton.click() await expect(absYButton).toBeDisabled()
await expect(absXButton).toBeDisabled() await page.waitForTimeout(100)
await page.waitForTimeout(100) await xAxisClick()
await yAxisClick() await page.keyboard.up('Shift')
await page.keyboard.up('Shift') await constrainButton.click()
await constrainButton.click() await expect(absYButton).not.toBeDisabled()
await expect(absXButton).not.toBeDisabled()
})
// clear selection by clicking on nothing // clear selection by clicking on nothing
await emptySpaceClick() await emptySpaceClick()
@ -194,7 +182,9 @@ test.describe('Testing selections', () => {
process.platform === 'linux' ? 'Control' : 'Meta' process.platform === 'linux' ? 'Control' : 'Meta'
) )
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.getByText(` |> xLine(${commonPoints.num2 * -1}, %)`).click() await page
.getByText(` |> lineTo([0, ${commonPoints.num3}], %)`)
.click()
await expect(page.locator('.cm-cursor')).toHaveCount(2) await expect(page.locator('.cm-cursor')).toHaveCount(2)
await page.waitForTimeout(500) await page.waitForTimeout(500)
@ -938,7 +928,6 @@ sketch002 = startSketchOn(extrude001, $seg01)
// test fillet button with the body in the scene // test fillet button with the body in the scene
const codeToAdd = `${await u.codeLocator.allInnerTexts()} const codeToAdd = `${await u.codeLocator.allInnerTexts()}
extrude001 = extrude(10, sketch001)` extrude001 = extrude(10, sketch001)`
await u.codeLocator.clear()
await u.codeLocator.fill(codeToAdd) await u.codeLocator.fill(codeToAdd)
await selectSegment() await selectSegment()
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled() await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()

View File

@ -258,7 +258,7 @@ test.describe('Testing settings', () => {
}) })
}) })
test.fixme( test(
`Project settings override user settings on desktop`, `Project settings override user settings on desktop`,
{ tag: ['@electron', '@skipWin'] }, { tag: ['@electron', '@skipWin'] },
async ({ browser: _ }, testInfo) => { async ({ browser: _ }, testInfo) => {
@ -318,6 +318,7 @@ test.describe('Testing settings', () => {
timeout: 5_000, timeout: 5_000,
}) })
.toContain(`themeColor = "${userThemeColor}"`) .toContain(`themeColor = "${userThemeColor}"`)
// Only close the button after we've confirmed
}) })
await test.step('Set project theme color', async () => { await test.step('Set project theme color', async () => {

View File

@ -1,6 +1,6 @@
{ {
"name": "zoo-modeling-app", "name": "zoo-modeling-app",
"version": "0.27.0", "version": "0.26.3",
"private": true, "private": true,
"productName": "Zoo Modeling App", "productName": "Zoo Modeling App",
"author": { "author": {
@ -40,7 +40,7 @@
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"decamelize": "^6.0.0", "decamelize": "^6.0.0",
"electron-squirrel-startup": "^1.0.1", "electron-squirrel-startup": "^1.0.1",
"electron-updater": "6.3.0", "electron-updater": "^6.3.9",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"html2canvas-pro": "^1.5.8", "html2canvas-pro": "^1.5.8",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",

View File

@ -141,7 +141,6 @@ export function Toolbar({
> >
{/* A menu item will either be a vertical line break, a button with a dropdown, or a single button */} {/* A menu item will either be a vertical line break, a button with a dropdown, or a single button */}
{currentModeItems.map((maybeIconConfig, i) => { {currentModeItems.map((maybeIconConfig, i) => {
// Vertical Line Break
if (maybeIconConfig === 'break') { if (maybeIconConfig === 'break') {
return ( return (
<div <div
@ -150,7 +149,6 @@ export function Toolbar({
/> />
) )
} else if (Array.isArray(maybeIconConfig)) { } else if (Array.isArray(maybeIconConfig)) {
// A button with a dropdown
return ( return (
<ActionButtonDropdown <ActionButtonDropdown
Element="button" Element="button"
@ -217,7 +215,6 @@ export function Toolbar({
} }
const itemConfig = maybeIconConfig const itemConfig = maybeIconConfig
// A single button
return ( return (
<div className="relative" key={itemConfig.id}> <div className="relative" key={itemConfig.id}>
<ActionButton <ActionButton

View File

@ -202,20 +202,12 @@ const Overlay = ({
let xAlignment = overlay.angle < 0 ? '0%' : '-100%' let xAlignment = overlay.angle < 0 ? '0%' : '-100%'
let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%' let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%'
// It's possible for the pathToNode to request a newer AST node
// than what's available in the AST at the moment of query.
// It eventually settles on being updated.
const _node1 = getNodeFromPath<Node<CallExpression>>( const _node1 = getNodeFromPath<Node<CallExpression>>(
kclManager.ast, kclManager.ast,
overlay.pathToNode, overlay.pathToNode,
'CallExpression' 'CallExpression'
) )
if (err(_node1)) return
// For that reason, to prevent console noise, we do not use err here.
if (_node1 instanceof Error) {
console.warn('ast older than pathToNode, not fatal, eventually settles', '')
return
}
const callExpression = _node1.node const callExpression = _node1.node
const constraints = getConstraintInfo( const constraints = getConstraintInfo(
@ -645,16 +637,10 @@ const ConstraintSymbol = ({
kclManager.ast, kclManager.ast,
kclManager.programMemory kclManager.programMemory
) )
if (!transform) return if (!transform) return
const { modifiedAst } = transform const { modifiedAst } = transform
// eslint-disable-next-line @typescript-eslint/no-floating-promises
await kclManager.updateAst(modifiedAst, true) kclManager.updateAst(modifiedAst, true)
// Code editor will be updated in the modelingMachine.
const newCode = recast(modifiedAst)
if (err(newCode)) return
await codeManager.updateCodeEditor(newCode)
} catch (e) { } catch (e) {
console.log('error', e) console.log('error', e)
} }

View File

@ -17,7 +17,6 @@ import {
Vector3, Vector3,
} from 'three' } from 'three'
import { import {
ANGLE_SNAP_THRESHOLD_DEGREES,
ARROWHEAD, ARROWHEAD,
AXIS_GROUP, AXIS_GROUP,
DRAFT_POINT, DRAFT_POINT,
@ -47,7 +46,6 @@ import {
VariableDeclaration, VariableDeclaration,
VariableDeclarator, VariableDeclarator,
sketchFromKclValue, sketchFromKclValue,
sketchFromKclValueOptional,
} from 'lang/wasm' } from 'lang/wasm'
import { import {
engineCommandManager, engineCommandManager,
@ -90,15 +88,13 @@ import { EngineCommandManager } from 'lang/std/engineConnection'
import { import {
getRectangleCallExpressions, getRectangleCallExpressions,
updateRectangleSketch, updateRectangleSketch,
updateCenterRectangleSketch,
} from 'lib/rectangleTool' } from 'lib/rectangleTool'
import { getThemeColorForThreeJs, Themes } from 'lib/theme' import { getThemeColorForThreeJs, Themes } from 'lib/theme'
import { err, Reason, reportRejection, trap } from 'lib/trap' import { err, reportRejection, trap } from 'lib/trap'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d' import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
import { SegmentInputs } from 'lang/std/stdTypes' import { SegmentInputs } from 'lang/std/stdTypes'
import { Node } from 'wasm-lib/kcl/bindings/Node' import { Node } from 'wasm-lib/kcl/bindings/Node'
import { radToDeg } from 'three/src/math/MathUtils'
type DraftSegment = 'line' | 'tangentialArcTo' type DraftSegment = 'line' | 'tangentialArcTo'
@ -455,7 +451,6 @@ export class SceneEntities {
const { modifiedAst } = addStartProfileAtRes const { modifiedAst } = addStartProfileAtRes
await kclManager.updateAst(modifiedAst, false) await kclManager.updateAst(modifiedAst, false)
this.removeIntersectionPlane() this.removeIntersectionPlane()
this.scene.remove(draftPointGroup) this.scene.remove(draftPointGroup)
@ -688,7 +683,7 @@ export class SceneEntities {
}) })
return nextAst return nextAst
} }
setupDraftSegment = async ( setUpDraftSegment = async (
sketchPathToNode: PathToNode, sketchPathToNode: PathToNode,
forward: [number, number, number], forward: [number, number, number],
up: [number, number, number], up: [number, number, number],
@ -803,24 +798,11 @@ export class SceneEntities {
(sceneObject) => sceneObject.object.name === X_AXIS (sceneObject) => sceneObject.object.name === X_AXIS
) )
const lastSegment = sketch.paths.slice(-1)[0] || sketch.start const lastSegment = sketch.paths.slice(-1)[0]
const snappedPoint = { const snappedPoint = {
x: intersectsYAxis ? 0 : intersection2d.x, x: intersectsYAxis ? 0 : intersection2d.x,
y: intersectsXAxis ? 0 : intersection2d.y, y: intersectsXAxis ? 0 : intersection2d.y,
} }
// Get the angle between the previous segment (or sketch start)'s end and this one's
const angle = Math.atan2(
snappedPoint.y - lastSegment.to[1],
snappedPoint.x - lastSegment.to[0]
)
const isHorizontal =
radToDeg(Math.abs(angle)) < ANGLE_SNAP_THRESHOLD_DEGREES ||
Math.abs(radToDeg(Math.abs(angle) - Math.PI)) <
ANGLE_SNAP_THRESHOLD_DEGREES
const isVertical =
Math.abs(radToDeg(Math.abs(angle) - Math.PI / 2)) <
ANGLE_SNAP_THRESHOLD_DEGREES
let resolvedFunctionName: ToolTip = 'line' let resolvedFunctionName: ToolTip = 'line'
@ -828,12 +810,6 @@ export class SceneEntities {
// case-based logic for different segment types // case-based logic for different segment types
if (lastSegment.type === 'TangentialArcTo') { if (lastSegment.type === 'TangentialArcTo') {
resolvedFunctionName = 'tangentialArcTo' resolvedFunctionName = 'tangentialArcTo'
} else if (isHorizontal) {
// If the angle between is 0 or 180 degrees (+/- the snapping angle), make the line an xLine
resolvedFunctionName = 'xLine'
} else if (isVertical) {
// If the angle between is 90 or 270 degrees (+/- the snapping angle), make the line a yLine
resolvedFunctionName = 'yLine'
} else if (snappedPoint.x === 0 || snappedPoint.y === 0) { } else if (snappedPoint.x === 0 || snappedPoint.y === 0) {
// We consider a point placed on axes or origin to be absolute // We consider a point placed on axes or origin to be absolute
resolvedFunctionName = 'lineTo' resolvedFunctionName = 'lineTo'
@ -859,11 +835,10 @@ export class SceneEntities {
} }
await kclManager.executeAstMock(modifiedAst) await kclManager.executeAstMock(modifiedAst)
if (intersectsProfileStart) { if (intersectsProfileStart) {
sceneInfra.modelingSend({ type: 'CancelSketch' }) sceneInfra.modelingSend({ type: 'CancelSketch' })
} else { } else {
await this.setupDraftSegment( await this.setUpDraftSegment(
sketchPathToNode, sketchPathToNode,
forward, forward,
up, up,
@ -871,8 +846,6 @@ export class SceneEntities {
segmentName segmentName
) )
} }
await codeManager.updateEditorWithAstAndWriteToFile(modifiedAst)
}, },
onMove: (args) => { onMove: (args) => {
this.onDragSegment({ this.onDragSegment({
@ -997,179 +970,8 @@ export class SceneEntities {
if (trap(_node)) return if (trap(_node)) return
const sketchInit = _node.node?.declarations?.[0]?.init const sketchInit = _node.node?.declarations?.[0]?.init
if (sketchInit.type !== 'PipeExpression') {
return
}
updateRectangleSketch(sketchInit, x, y, tags[0])
const newCode = recast(_ast)
let _recastAst = parse(newCode)
if (trap(_recastAst)) return
_ast = _recastAst
// Update the primary AST and unequip the rectangle tool
await kclManager.executeAstMock(_ast)
sceneInfra.modelingSend({ type: 'Finish rectangle' })
// lee: I had this at the bottom of the function, but it's
// possible sketchFromKclValue "fails" when sketching on a face,
// and this couldn't wouldn't run.
await codeManager.updateEditorWithAstAndWriteToFile(_ast)
const { execState } = await executeAst({
ast: _ast,
useFakeExecutor: true,
engineCommandManager: this.engineCommandManager,
programMemoryOverride,
idGenerator: kclManager.execState.idGenerator,
})
const programMemory = execState.memory
// Prepare to update the THREEjs scene
this.sceneProgramMemory = programMemory
const sketch = sketchFromKclValue(
programMemory.get(variableDeclarationName),
variableDeclarationName
)
if (err(sketch)) return
const sgPaths = sketch.paths
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
// Update the starting segment of the THREEjs scene
this.updateSegment(sketch.start, 0, 0, _ast, orthoFactor, sketch)
// Update the rest of the segments of the THREEjs scene
sgPaths.forEach((seg, index) =>
this.updateSegment(seg, index, 0, _ast, orthoFactor, sketch)
)
},
})
}
setupDraftCenterRectangle = async (
sketchPathToNode: PathToNode,
forward: [number, number, number],
up: [number, number, number],
sketchOrigin: [number, number, number],
rectangleOrigin: [x: number, y: number]
) => {
let _ast = structuredClone(kclManager.ast)
const _node1 = getNodeFromPath<VariableDeclaration>(
_ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node1)) return Promise.reject(_node1)
// startSketchOn already exists
const variableDeclarationName =
_node1.node?.declarations?.[0]?.id?.name || ''
const startSketchOn = _node1.node?.declarations
const startSketchOnInit = startSketchOn?.[0]?.init
const tags: [string, string, string] = [
findUniqueName(_ast, 'rectangleSegmentA'),
findUniqueName(_ast, 'rectangleSegmentB'),
findUniqueName(_ast, 'rectangleSegmentC'),
]
startSketchOn[0].init = createPipeExpression([
startSketchOnInit,
...getRectangleCallExpressions(rectangleOrigin, tags),
])
let _recastAst = parse(recast(_ast))
if (trap(_recastAst)) return Promise.reject(_recastAst)
_ast = _recastAst
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
sketchPathToNode,
forward,
up,
position: sketchOrigin,
maybeModdedAst: _ast,
draftExpressionsIndices: { start: 0, end: 3 },
})
sceneInfra.setCallbacks({
onMove: async (args) => {
// Update the width and height of the draft rectangle
const pathToNodeTwo = structuredClone(sketchPathToNode)
pathToNodeTwo[1][0] = 0
const _node = getNodeFromPath<VariableDeclaration>(
truncatedAst,
pathToNodeTwo || [],
'VariableDeclaration'
)
if (trap(_node)) return Promise.reject(_node)
const sketchInit = _node.node?.declarations?.[0]?.init
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
if (sketchInit.type === 'PipeExpression') { if (sketchInit.type === 'PipeExpression') {
updateCenterRectangleSketch( updateRectangleSketch(sketchInit, x, y, tags[0])
sketchInit,
x,
y,
tags[0],
rectangleOrigin[0],
rectangleOrigin[1]
)
}
const { execState } = await executeAst({
ast: truncatedAst,
useFakeExecutor: true,
engineCommandManager: this.engineCommandManager,
programMemoryOverride,
idGenerator: kclManager.execState.idGenerator,
})
const programMemory = execState.memory
this.sceneProgramMemory = programMemory
const sketch = sketchFromKclValue(
programMemory.get(variableDeclarationName),
variableDeclarationName
)
if (err(sketch)) return Promise.reject(sketch)
const sgPaths = sketch.paths
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
this.updateSegment(sketch.start, 0, 0, _ast, orthoFactor, sketch)
sgPaths.forEach((seg, index) =>
this.updateSegment(seg, index, 0, _ast, orthoFactor, sketch)
)
},
onClick: async (args) => {
// If there is a valid camera interaction that matches, do that instead
const interaction = sceneInfra.camControls.getInteractionType(
args.mouseEvent
)
if (interaction !== 'none') return
// Commit the rectangle to the full AST/code and return to sketch.idle
const cornerPoint = args.intersectionPoint?.twoD
if (!cornerPoint || args.mouseEvent.button !== 0) return
const x = roundOff((cornerPoint.x || 0) - rectangleOrigin[0])
const y = roundOff((cornerPoint.y || 0) - rectangleOrigin[1])
const _node = getNodeFromPath<VariableDeclaration>(
_ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node)) return
const sketchInit = _node.node?.declarations?.[0]?.init
if (sketchInit.type === 'PipeExpression') {
updateCenterRectangleSketch(
sketchInit,
x,
y,
tags[0],
rectangleOrigin[0],
rectangleOrigin[1]
)
let _recastAst = parse(recast(_ast)) let _recastAst = parse(recast(_ast))
if (trap(_recastAst)) return if (trap(_recastAst)) return
@ -1177,12 +979,7 @@ export class SceneEntities {
// Update the primary AST and unequip the rectangle tool // Update the primary AST and unequip the rectangle tool
await kclManager.executeAstMock(_ast) await kclManager.executeAstMock(_ast)
sceneInfra.modelingSend({ type: 'Finish center rectangle' }) sceneInfra.modelingSend({ type: 'Finish rectangle' })
// lee: I had this at the bottom of the function, but it's
// possible sketchFromKclValue "fails" when sketching on a face,
// and this couldn't wouldn't run.
await codeManager.updateEditorWithAstAndWriteToFile(_ast)
const { execState } = await executeAst({ const { execState } = await executeAst({
ast: _ast, ast: _ast,
@ -1369,17 +1166,13 @@ export class SceneEntities {
if (err(moddedResult)) return if (err(moddedResult)) return
modded = moddedResult.modifiedAst modded = moddedResult.modifiedAst
const newCode = recast(modded) let _recastAst = parse(recast(modded))
if (err(newCode)) return
let _recastAst = parse(newCode)
if (trap(_recastAst)) return Promise.reject(_recastAst) if (trap(_recastAst)) return Promise.reject(_recastAst)
_ast = _recastAst _ast = _recastAst
// Update the primary AST and unequip the rectangle tool // Update the primary AST and unequip the rectangle tool
await kclManager.executeAstMock(_ast) await kclManager.executeAstMock(_ast)
sceneInfra.modelingSend({ type: 'Finish circle' }) sceneInfra.modelingSend({ type: 'Finish circle' })
await codeManager.updateEditorWithAstAndWriteToFile(_ast)
} }
}, },
}) })
@ -1415,7 +1208,6 @@ export class SceneEntities {
forward, forward,
position, position,
}) })
await codeManager.writeToFile()
} }
}, },
onDrag: async ({ onDrag: async ({
@ -1698,13 +1490,10 @@ export class SceneEntities {
this.sceneProgramMemory = programMemory this.sceneProgramMemory = programMemory
const maybeSketch = programMemory.get(variableDeclarationName) const maybeSketch = programMemory.get(variableDeclarationName)
let sketch: Sketch | undefined let sketch = undefined
const sk = sketchFromKclValueOptional( const sg = sketchFromKclValue(maybeSketch, variableDeclarationName)
maybeSketch, if (!err(sg)) {
variableDeclarationName sketch = sg
)
if (!(sk instanceof Reason)) {
sketch = sk
} else if ((maybeSketch as Solid).sketch) { } else if ((maybeSketch as Solid).sketch) {
sketch = (maybeSketch as Solid).sketch sketch = (maybeSketch as Solid).sketch
} }

View File

@ -50,8 +50,6 @@ export const RAYCASTABLE_PLANE = 'raycastable-plane'
export const X_AXIS = 'xAxis' export const X_AXIS = 'xAxis'
export const Y_AXIS = 'yAxis' export const Y_AXIS = 'yAxis'
/** If a segment angle is less than this many degrees off a meanginful angle it'll snap to it */
export const ANGLE_SNAP_THRESHOLD_DEGREES = 3
/** the THREEjs representation of the group surrounding a "snapped" point that is not yet placed */ /** the THREEjs representation of the group surrounding a "snapped" point that is not yet placed */
export const DRAFT_POINT_GROUP = 'draft-point-group' export const DRAFT_POINT_GROUP = 'draft-point-group'
/** the THREEjs representation of a "snapped" point that is not yet placed */ /** the THREEjs representation of a "snapped" point that is not yet placed */

View File

@ -145,7 +145,7 @@ export function useCalc({
const _programMem: ProgramMemory = ProgramMemory.empty() const _programMem: ProgramMemory = ProgramMemory.empty()
for (const { key, value } of availableVarInfo.variables) { for (const { key, value } of availableVarInfo.variables) {
const error = _programMem.set(key, { const error = _programMem.set(key, {
type: 'String', type: 'UserVal',
value, value,
__meta: [], __meta: [],
}) })

View File

@ -22,7 +22,6 @@ import usePlatform from 'hooks/usePlatform'
import { FileEntry } from 'lib/project' import { FileEntry } from 'lib/project'
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher' import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
import { normalizeLineEndings } from 'lib/codeEditor' import { normalizeLineEndings } from 'lib/codeEditor'
import { reportRejection } from 'lib/trap'
function getIndentationCSS(level: number) { function getIndentationCSS(level: number) {
return `calc(1rem * ${level + 1})` return `calc(1rem * ${level + 1})`
@ -197,7 +196,8 @@ const FileTreeItem = ({
return return
} }
if (isCurrentFile && eventType === 'change') { // Don't try to read a file that was removed.
if (isCurrentFile && eventType !== 'unlink') {
let code = await window.electron.readFile(path, { encoding: 'utf-8' }) let code = await window.electron.readFile(path, { encoding: 'utf-8' })
code = normalizeLineEndings(code) code = normalizeLineEndings(code)
codeManager.updateCodeStateEditor(code) codeManager.updateCodeStateEditor(code)
@ -242,7 +242,7 @@ const FileTreeItem = ({
// Show the renaming form // Show the renaming form
addCurrentItemToRenaming() addCurrentItemToRenaming()
} else if (e.code === 'Space') { } else if (e.code === 'Space') {
void handleClick().catch(reportRejection) void handleClick()
} }
} }
@ -293,7 +293,7 @@ const FileTreeItem = ({
style={{ paddingInlineStart: getIndentationCSS(level) }} style={{ paddingInlineStart: getIndentationCSS(level) }}
onClick={(e) => { onClick={(e) => {
e.currentTarget.focus() e.currentTarget.focus()
void handleClick().catch(reportRejection) void handleClick()
}} }}
onKeyUp={handleKeyUp} onKeyUp={handleKeyUp}
> >

View File

@ -63,7 +63,6 @@ import {
import { import {
moveValueIntoNewVariablePath, moveValueIntoNewVariablePath,
sketchOnExtrudedFace, sketchOnExtrudedFace,
sketchOnOffsetPlane,
startSketchOnDefault, startSketchOnDefault,
} from 'lang/modifyAst' } from 'lang/modifyAst'
import { Program, parse, recast } from 'lang/wasm' import { Program, parse, recast } from 'lang/wasm'
@ -305,7 +304,6 @@ export const ModelingMachineProvider = ({
const dispatchSelection = (selection?: EditorSelection) => { const dispatchSelection = (selection?: EditorSelection) => {
if (!selection) return // TODO less of hack for the below please if (!selection) return // TODO less of hack for the below please
if (!editorManager.editorView) return if (!editorManager.editorView) return
setTimeout(() => { setTimeout(() => {
if (!editorManager.editorView) return if (!editorManager.editorView) return
editorManager.editorView.dispatch({ editorManager.editorView.dispatch({
@ -484,7 +482,7 @@ export const ModelingMachineProvider = ({
engineCommandManager.exportInfo = { engineCommandManager.exportInfo = {
intent: ExportIntent.Save, intent: ExportIntent.Save,
// This never gets used its only for make. // This never gets used its only for make.
name: file?.name?.replace('.kcl', `.${event.data.type}`) || '', name: '',
} }
const format = { const format = {
@ -637,16 +635,13 @@ export const ModelingMachineProvider = ({
), ),
'animate-to-face': fromPromise(async ({ input }) => { 'animate-to-face': fromPromise(async ({ input }) => {
if (!input) return undefined if (!input) return undefined
if (input.type === 'extrudeFace' || input.type === 'offsetPlane') { if (input.type === 'extrudeFace') {
const sketched = const sketched = sketchOnExtrudedFace(
input.type === 'extrudeFace' kclManager.ast,
? sketchOnExtrudedFace( input.sketchPathToNode,
kclManager.ast, input.extrudePathToNode,
input.sketchPathToNode, input.faceInfo
input.extrudePathToNode, )
input.faceInfo
)
: sketchOnOffsetPlane(kclManager.ast, input.pathToNode)
if (err(sketched)) { if (err(sketched)) {
const sketchedError = new Error( const sketchedError = new Error(
'Incompatible face, please try another' 'Incompatible face, please try another'
@ -658,9 +653,10 @@ export const ModelingMachineProvider = ({
await kclManager.executeAstMock(modifiedAst) await kclManager.executeAstMock(modifiedAst)
const id = await letEngineAnimateAndSyncCamAfter(
input.type === 'extrudeFace' ? input.faceId : input.planeId engineCommandManager,
await letEngineAnimateAndSyncCamAfter(engineCommandManager, id) input.faceId
)
sceneInfra.camControls.syncDirection = 'clientToEngine' sceneInfra.camControls.syncDirection = 'clientToEngine'
return { return {
sketchPathToNode: pathToNewSketchNode, sketchPathToNode: pathToNewSketchNode,
@ -736,11 +732,6 @@ export const ModelingMachineProvider = ({
sketchDetails.origin sketchDetails.origin
) )
if (err(updatedAst)) return Promise.reject(updatedAst) if (err(updatedAst)) return Promise.reject(updatedAst)
await codeManager.updateEditorWithAstAndWriteToFile(
updatedAst.newAst
)
const selection = updateSelections( const selection = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -777,11 +768,6 @@ export const ModelingMachineProvider = ({
sketchDetails.origin sketchDetails.origin
) )
if (err(updatedAst)) return Promise.reject(updatedAst) if (err(updatedAst)) return Promise.reject(updatedAst)
await codeManager.updateEditorWithAstAndWriteToFile(
updatedAst.newAst
)
const selection = updateSelections( const selection = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -827,11 +813,6 @@ export const ModelingMachineProvider = ({
sketchDetails.origin sketchDetails.origin
) )
if (err(updatedAst)) return Promise.reject(updatedAst) if (err(updatedAst)) return Promise.reject(updatedAst)
await codeManager.updateEditorWithAstAndWriteToFile(
updatedAst.newAst
)
const selection = updateSelections( const selection = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -865,11 +846,6 @@ export const ModelingMachineProvider = ({
sketchDetails.origin sketchDetails.origin
) )
if (err(updatedAst)) return Promise.reject(updatedAst) if (err(updatedAst)) return Promise.reject(updatedAst)
await codeManager.updateEditorWithAstAndWriteToFile(
updatedAst.newAst
)
const selection = updateSelections( const selection = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -905,11 +881,6 @@ export const ModelingMachineProvider = ({
sketchDetails.origin sketchDetails.origin
) )
if (err(updatedAst)) return Promise.reject(updatedAst) if (err(updatedAst)) return Promise.reject(updatedAst)
await codeManager.updateEditorWithAstAndWriteToFile(
updatedAst.newAst
)
const selection = updateSelections( const selection = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -946,11 +917,6 @@ export const ModelingMachineProvider = ({
sketchDetails.origin sketchDetails.origin
) )
if (err(updatedAst)) return Promise.reject(updatedAst) if (err(updatedAst)) return Promise.reject(updatedAst)
await codeManager.updateEditorWithAstAndWriteToFile(
updatedAst.newAst
)
const selection = updateSelections( const selection = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -987,11 +953,6 @@ export const ModelingMachineProvider = ({
sketchDetails.origin sketchDetails.origin
) )
if (err(updatedAst)) return Promise.reject(updatedAst) if (err(updatedAst)) return Promise.reject(updatedAst)
await codeManager.updateEditorWithAstAndWriteToFile(
updatedAst.newAst
)
const selection = updateSelections( const selection = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -1038,11 +999,6 @@ export const ModelingMachineProvider = ({
sketchDetails.origin sketchDetails.origin
) )
if (err(updatedAst)) return Promise.reject(updatedAst) if (err(updatedAst)) return Promise.reject(updatedAst)
await codeManager.updateEditorWithAstAndWriteToFile(
updatedAst.newAst
)
const selection = updateSelections( const selection = updateSelections(
{ 0: pathToReplacedNode }, { 0: pathToReplacedNode },
selectionRanges, selectionRanges,

View File

@ -43,7 +43,6 @@ import {
completionKeymap, completionKeymap,
} from '@codemirror/autocomplete' } from '@codemirror/autocomplete'
import CodeEditor from './CodeEditor' import CodeEditor from './CodeEditor'
import { codeManagerHistoryCompartment } from 'lang/codeManager'
export const editorShortcutMeta = { export const editorShortcutMeta = {
formatCode: { formatCode: {
@ -90,7 +89,7 @@ export const KclEditorPane = () => {
cursorBlinkRate: cursorBlinking.current ? 1200 : 0, cursorBlinkRate: cursorBlinking.current ? 1200 : 0,
}), }),
lineHighlightField, lineHighlightField,
codeManagerHistoryCompartment.of(history()), history(),
closeBrackets(), closeBrackets(),
codeFolding(), codeFolding(),
keymap.of([ keymap.of([
@ -122,6 +121,7 @@ export const KclEditorPane = () => {
lineNumbers(), lineNumbers(),
highlightActiveLineGutter(), highlightActiveLineGutter(),
highlightSpecialChars(), highlightSpecialChars(),
history(),
foldGutter(), foldGutter(),
EditorState.allowMultipleSelections.of(true), EditorState.allowMultipleSelections.of(true),
indentOnInput(), indentOnInput(),

View File

@ -5,12 +5,12 @@ import {
ProgramMemory, ProgramMemory,
Path, Path,
ExtrudeSurface, ExtrudeSurface,
sketchFromKclValueOptional, sketchFromKclValue,
} from 'lang/wasm' } from 'lang/wasm'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { useResolvedTheme } from 'hooks/useResolvedTheme' import { useResolvedTheme } from 'hooks/useResolvedTheme'
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import { Reason, trap } from 'lib/trap' import { err, trap } from 'lib/trap'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
@ -89,17 +89,17 @@ export const processMemory = (programMemory: ProgramMemory) => {
const processedMemory: any = {} const processedMemory: any = {}
for (const [key, val] of programMemory?.visibleEntries()) { for (const [key, val] of programMemory?.visibleEntries()) {
if ( if (
val.type === 'Sketch' || (val.type === 'UserVal' && val.value.type === 'Sketch') ||
// @ts-ignore // @ts-ignore
val.type !== 'Function' (val.type !== 'Function' && val.type !== 'UserVal')
) { ) {
const sk = sketchFromKclValueOptional(val, key) const sg = sketchFromKclValue(val, key)
if (val.type === 'Solid') { if (val.type === 'Solid') {
processedMemory[key] = val.value.map(({ ...rest }: ExtrudeSurface) => { processedMemory[key] = val.value.map(({ ...rest }: ExtrudeSurface) => {
return rest return rest
}) })
} else if (!(sk instanceof Reason)) { } else if (!err(sg)) {
processedMemory[key] = sk.paths.map(({ __geoMeta, ...rest }: Path) => { processedMemory[key] = sg.paths.map(({ __geoMeta, ...rest }: Path) => {
return rest return rest
}) })
} else { } else {
@ -110,6 +110,8 @@ export const processMemory = (programMemory: ProgramMemory) => {
processedMemory[key] = `__function(${(val as any)?.expression?.params processedMemory[key] = `__function(${(val as any)?.expression?.params
?.map?.(({ identifier }: any) => identifier?.name || '') ?.map?.(({ identifier }: any) => identifier?.name || '')
.join(', ')})__` .join(', ')})__`
} else {
processedMemory[key] = val.value
} }
} }
return processedMemory return processedMemory

View File

@ -1,81 +0,0 @@
import { editorManager } from 'lib/singletons'
import { Diagnostic } from '@codemirror/lint'
describe('EditorManager Class', () => {
describe('makeUniqueDiagnostics', () => {
it('should filter out duplicated diagnostics', () => {
const duplicatedDiagnostics: Diagnostic[] = [
{
from: 2,
to: 10,
severity: 'hint',
message: 'my cool message',
},
{
from: 2,
to: 10,
severity: 'hint',
message: 'my cool message',
},
{
from: 2,
to: 10,
severity: 'hint',
message: 'my cool message',
},
]
const expected: Diagnostic[] = [
{
from: 2,
to: 10,
severity: 'hint',
message: 'my cool message',
},
]
const actual = editorManager.makeUniqueDiagnostics(duplicatedDiagnostics)
expect(actual).toStrictEqual(expected)
})
it('should filter out duplicated diagnostic and keep some original ones', () => {
const duplicatedDiagnostics: Diagnostic[] = [
{
from: 0,
to: 10,
severity: 'hint',
message: 'my cool message',
},
{
from: 0,
to: 10,
severity: 'hint',
message: 'my cool message',
},
{
from: 88,
to: 99,
severity: 'hint',
message: 'my super cool message',
},
]
const expected: Diagnostic[] = [
{
from: 0,
to: 10,
severity: 'hint',
message: 'my cool message',
},
{
from: 88,
to: 99,
severity: 'hint',
message: 'my super cool message',
},
]
const actual = editorManager.makeUniqueDiagnostics(duplicatedDiagnostics)
expect(actual).toStrictEqual(expected)
})
})
})

View File

@ -24,6 +24,10 @@ export const modelingMachineEvent = modelingMachineAnnotation.of(true)
const setDiagnosticsAnnotation = Annotation.define<boolean>() const setDiagnosticsAnnotation = Annotation.define<boolean>()
export const setDiagnosticsEvent = setDiagnosticsAnnotation.of(true) export const setDiagnosticsEvent = setDiagnosticsAnnotation.of(true)
function diagnosticIsEqual(d1: Diagnostic, d2: Diagnostic): boolean {
return d1.from === d2.from && d1.to === d2.to && d1.message === d2.message
}
export default class EditorManager { export default class EditorManager {
private _editorView: EditorView | null = null private _editorView: EditorView | null = null
private _copilotEnabled: boolean = true private _copilotEnabled: boolean = true
@ -68,10 +72,9 @@ export default class EditorManager {
// we cannot use <>.constructor.name since it will get destroyed // we cannot use <>.constructor.name since it will get destroyed
// when packaging the application. // when packaging the application.
const isTreeHighlightPlugin = const isTreeHighlightPlugin =
e?.value && e.value.hasOwnProperty('tree') &&
e.value?.hasOwnProperty('tree') && e.value.hasOwnProperty('decoratedTo') &&
e.value?.hasOwnProperty('decoratedTo') && e.value.hasOwnProperty('decorations')
e.value?.hasOwnProperty('decorations')
if (isTreeHighlightPlugin) { if (isTreeHighlightPlugin) {
let originalUpdate = e.value.update let originalUpdate = e.value.update
@ -158,29 +161,20 @@ export default class EditorManager {
} }
} }
/**
* Given an array of Diagnostics remove any duplicates by hashing a key
* in the format of from + ' ' + to + ' ' + message.
*/
makeUniqueDiagnostics(duplicatedDiagnostics: Diagnostic[]): Diagnostic[] {
const uniqueDiagnostics: Diagnostic[] = []
const seenDiagnostic: { [key: string]: boolean } = {}
duplicatedDiagnostics.forEach((diagnostic: Diagnostic) => {
const hash = `${diagnostic.from} ${diagnostic.to} ${diagnostic.message}`
if (!seenDiagnostic[hash]) {
uniqueDiagnostics.push(diagnostic)
seenDiagnostic[hash] = true
}
})
return uniqueDiagnostics
}
setDiagnostics(diagnostics: Diagnostic[]): void { setDiagnostics(diagnostics: Diagnostic[]): void {
if (!this._editorView) return if (!this._editorView) return
// Clear out any existing diagnostics that are the same. // Clear out any existing diagnostics that are the same.
diagnostics = this.makeUniqueDiagnostics(diagnostics) for (const diagnostic of diagnostics) {
for (const otherDiagnostic of diagnostics) {
if (diagnosticIsEqual(diagnostic, otherDiagnostic)) {
diagnostics = diagnostics.filter(
(d) => !diagnosticIsEqual(d, diagnostic)
)
diagnostics.push(diagnostic)
break
}
}
}
this._editorView.dispatch({ this._editorView.dispatch({
effects: [setDiagnosticsEffect.of(diagnostics)], effects: [setDiagnosticsEffect.of(diagnostics)],

View File

@ -88,10 +88,6 @@ export function useEngineConnectionSubscriptions() {
? [codeRef.range] ? [codeRef.range]
: [codeRef.range, consumedCodeRef.range] : [codeRef.range, consumedCodeRef.range]
) )
} else if (artifact?.type === 'plane') {
const codeRef = artifact.codeRef
if (err(codeRef)) return
editorManager.setHighlightRange([codeRef.range])
} else { } else {
editorManager.setHighlightRange([[0, 0]]) editorManager.setHighlightRange([[0, 0]])
} }
@ -190,42 +186,8 @@ export function useEngineConnectionSubscriptions() {
}) })
return return
} }
const artifact =
engineCommandManager.artifactGraph.get(planeOrFaceId)
if (artifact?.type === 'plane') {
const planeInfo = await getFaceDetails(planeOrFaceId)
sceneInfra.modelingSend({
type: 'Select default plane',
data: {
type: 'offsetPlane',
zAxis: [
planeInfo.z_axis.x,
planeInfo.z_axis.y,
planeInfo.z_axis.z,
],
yAxis: [
planeInfo.y_axis.x,
planeInfo.y_axis.y,
planeInfo.y_axis.z,
],
position: [
planeInfo.origin.x,
planeInfo.origin.y,
planeInfo.origin.z,
].map((num) => num / sceneInfra._baseUnitMultiplier) as [
number,
number,
number
],
planeId: planeOrFaceId,
pathToNode: artifact.codeRef.pathToNode,
},
})
}
// Artifact is likely an extrusion face
const faceId = planeOrFaceId const faceId = planeOrFaceId
const artifact = engineCommandManager.artifactGraph.get(faceId)
const extrusion = getSweepFromSuspectedSweepSurface( const extrusion = getSweepFromSuspectedSweepSurface(
faceId, faceId,
engineCommandManager.artifactGraph engineCommandManager.artifactGraph

View File

@ -2,13 +2,13 @@ import {
SetVarNameModal, SetVarNameModal,
createSetVarNameModal, createSetVarNameModal,
} from 'components/SetVarNameModal' } from 'components/SetVarNameModal'
import { editorManager, kclManager, codeManager } from 'lib/singletons' import { editorManager, kclManager } from 'lib/singletons'
import { reportRejection, trap, err } from 'lib/trap' import { reportRejection, trap } from 'lib/trap'
import { moveValueIntoNewVariable } from 'lang/modifyAst' import { moveValueIntoNewVariable } from 'lang/modifyAst'
import { isNodeSafeToReplace } from 'lang/queryAst' import { isNodeSafeToReplace } from 'lang/queryAst'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useModelingContext } from './useModelingContext' import { useModelingContext } from './useModelingContext'
import { PathToNode, SourceRange, recast } from 'lang/wasm' import { PathToNode, SourceRange } from 'lang/wasm'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { toSync } from 'lib/utils' import { toSync } from 'lib/utils'
@ -57,11 +57,6 @@ export function useConvertToVariable(range?: SourceRange) {
) )
await kclManager.updateAst(_modifiedAst, true) await kclManager.updateAst(_modifiedAst, true)
const newCode = recast(_modifiedAst)
if (err(newCode)) return
codeManager.updateCodeEditor(newCode)
return pathToReplacedNode return pathToReplacedNode
} catch (e) { } catch (e) {
console.log('error', e) console.log('error', e)

View File

@ -125,7 +125,7 @@ export class KclManager {
if (this.lints.length > 0) { if (this.lints.length > 0) {
diagnostics = diagnostics.concat(this.lints) diagnostics = diagnostics.concat(this.lints)
} }
editorManager?.setDiagnostics(diagnostics) editorManager.setDiagnostics(diagnostics)
} }
addKclErrors(kclErrors: KCLError[]) { addKclErrors(kclErrors: KCLError[]) {
@ -357,6 +357,9 @@ export class KclManager {
this.clearAst() this.clearAst()
return return
} }
codeManager.updateCodeEditor(newCode)
// Write the file to disk.
await codeManager.writeToFile()
this._ast = { ...newAst } this._ast = { ...newAst }
const { logs, errors, execState } = await executeAst({ const { logs, errors, execState } = await executeAst({
@ -494,6 +497,11 @@ export class KclManager {
} }
if (execute) { if (execute) {
// Call execute on the set ast.
// Update the code state and editor.
codeManager.updateCodeEditor(newCode)
// Write the file to disk.
await codeManager.writeToFile()
await this.executeAst({ await this.executeAst({
ast: astWithUpdatedSource, ast: astWithUpdatedSource,
zoomToFit: optionalParams?.zoomToFit, zoomToFit: optionalParams?.zoomToFit,

View File

@ -18,7 +18,8 @@ const mySketch001 = startSketchOn('XY')
// @ts-ignore // @ts-ignore
const sketch001 = execState.memory.get('mySketch001') const sketch001 = execState.memory.get('mySketch001')
expect(sketch001).toEqual({ expect(sketch001).toEqual({
type: 'Sketch', type: 'UserVal',
__meta: [{ sourceRange: [46, 71, 0] }],
value: { value: {
type: 'Sketch', type: 'Sketch',
on: expect.any(Object), on: expect.any(Object),

View File

@ -6,17 +6,12 @@ import { isDesktop } from 'lib/isDesktop'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { editorManager } from 'lib/singletons' import { editorManager } from 'lib/singletons'
import { Annotation, Transaction } from '@codemirror/state' import { Annotation, Transaction } from '@codemirror/state'
import { EditorView, KeyBinding } from '@codemirror/view' import { KeyBinding } from '@codemirror/view'
import { recast, Program } from 'lang/wasm'
import { err } from 'lib/trap'
import { Compartment } from '@codemirror/state'
import { history } from '@codemirror/commands'
const PERSIST_CODE_KEY = 'persistCode' const PERSIST_CODE_KEY = 'persistCode'
const codeManagerUpdateAnnotation = Annotation.define<boolean>() const codeManagerUpdateAnnotation = Annotation.define<boolean>()
export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(true) export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(true)
export const codeManagerHistoryCompartment = new Compartment()
export default class CodeManager { export default class CodeManager {
private _code: string = bracket private _code: string = bracket
@ -93,12 +88,9 @@ export default class CodeManager {
/** /**
* Update the code in the editor. * Update the code in the editor.
*/ */
updateCodeEditor(code: string, clearHistory?: boolean): void { updateCodeEditor(code: string): void {
this.code = code this.code = code
if (editorManager.editorView) { if (editorManager.editorView) {
if (clearHistory) {
clearCodeMirrorHistory(editorManager.editorView)
}
editorManager.editorView.dispatch({ editorManager.editorView.dispatch({
changes: { changes: {
from: 0, from: 0,
@ -107,7 +99,7 @@ export default class CodeManager {
}, },
annotations: [ annotations: [
codeManagerUpdateEvent, codeManagerUpdateEvent,
Transaction.addToHistory.of(!clearHistory), Transaction.addToHistory.of(true),
], ],
}) })
} }
@ -116,11 +108,11 @@ export default class CodeManager {
/** /**
* Update the code, state, and the code the code mirror editor sees. * Update the code, state, and the code the code mirror editor sees.
*/ */
updateCodeStateEditor(code: string, clearHistory?: boolean): void { updateCodeStateEditor(code: string): void {
if (this._code !== code) { if (this._code !== code) {
this.code = code this.code = code
this.#updateState(code) this.#updateState(code)
this.updateCodeEditor(code, clearHistory) this.updateCodeEditor(code)
} }
} }
@ -155,13 +147,6 @@ export default class CodeManager {
safeLSSetItem(PERSIST_CODE_KEY, this.code) safeLSSetItem(PERSIST_CODE_KEY, this.code)
} }
} }
async updateEditorWithAstAndWriteToFile(ast: Program) {
const newCode = recast(ast)
if (err(newCode)) return
this.updateCodeStateEditor(newCode)
await this.writeToFile()
}
} }
function safeLSGetItem(key: string) { function safeLSGetItem(key: string) {
@ -173,17 +158,3 @@ function safeLSSetItem(key: string, value: string) {
if (typeof window === 'undefined') return if (typeof window === 'undefined') return
localStorage?.setItem(key, value) localStorage?.setItem(key, value)
} }
function clearCodeMirrorHistory(view: EditorView) {
// Clear history
view.dispatch({
effects: [codeManagerHistoryCompartment.reconfigure([])],
annotations: [codeManagerUpdateEvent],
})
// Add history back
view.dispatch({
effects: [codeManagerHistoryCompartment.reconfigure([history()])],
annotations: [codeManagerUpdateEvent],
})
}

View File

@ -58,13 +58,7 @@ const newVar = myVar + 1`
` `
const mem = await exe(code) const mem = await exe(code)
// geo is three js buffer geometry and is very bloated to have in tests // geo is three js buffer geometry and is very bloated to have in tests
const sk = mem.get('mySketch') const minusGeo = mem.get('mySketch')?.value?.paths
expect(sk?.type).toEqual('Sketch')
if (sk?.type !== 'Sketch') {
return
}
const minusGeo = sk?.value?.paths
expect(minusGeo).toEqual([ expect(minusGeo).toEqual([
{ {
type: 'ToPoint', type: 'ToPoint',
@ -156,7 +150,7 @@ const newVar = myVar + 1`
].join('\n') ].join('\n')
const mem = await exe(code) const mem = await exe(code)
expect(mem.get('mySk1')).toEqual({ expect(mem.get('mySk1')).toEqual({
type: 'Sketch', type: 'UserVal',
value: { value: {
type: 'Sketch', type: 'Sketch',
on: expect.any(Object), on: expect.any(Object),
@ -221,6 +215,7 @@ const newVar = myVar + 1`
id: expect.any(String), id: expect.any(String),
__meta: [{ sourceRange: [39, 63, 0] }], __meta: [{ sourceRange: [39, 63, 0] }],
}, },
__meta: [{ sourceRange: [39, 63, 0] }],
}) })
}) })
it('execute array expression', async () => { it('execute array expression', async () => {
@ -230,7 +225,7 @@ const newVar = myVar + 1`
const mem = await exe(code) const mem = await exe(code)
// TODO path to node is probably wrong here, zero indexes are not correct // TODO path to node is probably wrong here, zero indexes are not correct
expect(mem.get('three')).toEqual({ expect(mem.get('three')).toEqual({
type: 'Int', type: 'UserVal',
value: 3, value: 3,
__meta: [ __meta: [
{ {
@ -239,17 +234,8 @@ const newVar = myVar + 1`
], ],
}) })
expect(mem.get('yo')).toEqual({ expect(mem.get('yo')).toEqual({
type: 'Array', type: 'UserVal',
value: [ value: [1, '2', 3, 9],
{ type: 'Int', value: 1, __meta: [{ sourceRange: [28, 29, 0] }] },
{ type: 'String', value: '2', __meta: [{ sourceRange: [31, 34, 0] }] },
{ type: 'Int', value: 3, __meta: [{ sourceRange: [14, 15, 0] }] },
{
type: 'Number',
value: 9,
__meta: [{ sourceRange: [43, 44, 0] }, { sourceRange: [47, 48, 0] }],
},
],
__meta: [ __meta: [
{ {
sourceRange: [27, 49, 0], sourceRange: [27, 49, 0],
@ -267,25 +253,8 @@ const newVar = myVar + 1`
].join('\n') ].join('\n')
const mem = await exe(code) const mem = await exe(code)
expect(mem.get('yo')).toEqual({ expect(mem.get('yo')).toEqual({
type: 'Object', type: 'UserVal',
value: { value: { aStr: 'str', anum: 2, identifier: 3, binExp: 9 },
aStr: {
type: 'String',
value: 'str',
__meta: [{ sourceRange: [34, 39, 0] }],
},
anum: { type: 'Int', value: 2, __meta: [{ sourceRange: [47, 48, 0] }] },
identifier: {
type: 'Int',
value: 3,
__meta: [{ sourceRange: [14, 15, 0] }],
},
binExp: {
type: 'Number',
value: 9,
__meta: [{ sourceRange: [77, 78, 0] }, { sourceRange: [81, 82, 0] }],
},
},
__meta: [ __meta: [
{ {
sourceRange: [27, 83, 0], sourceRange: [27, 83, 0],
@ -299,11 +268,11 @@ const newVar = myVar + 1`
) )
const mem = await exe(code) const mem = await exe(code)
expect(mem.get('myVar')).toEqual({ expect(mem.get('myVar')).toEqual({
type: 'String', type: 'UserVal',
value: '123', value: '123',
__meta: [ __meta: [
{ {
sourceRange: [19, 24, 0], sourceRange: [41, 50, 0],
}, },
], ],
}) })
@ -387,26 +356,7 @@ describe('testing math operators', () => {
it('with unaryExpression in ArrayExpression', async () => { it('with unaryExpression in ArrayExpression', async () => {
const code = 'const myVar = [1,-legLen(5, 4)]' const code = 'const myVar = [1,-legLen(5, 4)]'
const mem = await exe(code) const mem = await exe(code)
expect(mem.get('myVar')?.value).toEqual([ expect(mem.get('myVar')?.value).toEqual([1, -3])
{
__meta: [
{
sourceRange: [15, 16, 0],
},
],
type: 'Int',
value: 1,
},
{
__meta: [
{
sourceRange: [17, 30, 0],
},
],
type: 'Number',
value: -3,
},
])
}) })
it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => { it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => {
const code = [ const code = [

View File

@ -55,13 +55,18 @@ describe('Test KCL Samples from public Github repository', () => {
}) })
// Run through all of the files in the manifest json. This will allow us to be automatically updated // Run through all of the files in the manifest json. This will allow us to be automatically updated
// with the latest changes in github. We won't be hard coding the filenames // with the latest changes in github. We won't be hard coding the filenames
files.forEach((file: KclSampleFile) => { it(
it(`should parse ${file.filename} without errors`, async () => { 'should run through all the files',
const code = await getKclSampleCodeFromGithub(file.filename) async () => {
const parsed = parse(code) for (let i = 0; i < files.length; i++) {
assert(!(parsed instanceof Error)) const file: KclSampleFile = files[i]
}, 1000) const code = await getKclSampleCodeFromGithub(file.filename)
}) const parsed = parse(code)
assert(!(parsed instanceof Error))
}
},
files.length * 1000
)
}) })
describe('when performing enginelessExecutor', () => { describe('when performing enginelessExecutor', () => {

View File

@ -8,7 +8,6 @@ import {
VariableDeclarator, VariableDeclarator,
Expr, Expr,
Literal, Literal,
LiteralValue,
PipeSubstitution, PipeSubstitution,
Identifier, Identifier,
ArrayExpression, ArrayExpression,
@ -19,7 +18,6 @@ import {
ProgramMemory, ProgramMemory,
SourceRange, SourceRange,
sketchFromKclValue, sketchFromKclValue,
isPathToNodeNumber,
} from './wasm' } from './wasm'
import { import {
isNodeSafeToReplacePath, isNodeSafeToReplacePath,
@ -527,60 +525,6 @@ export function sketchOnExtrudedFace(
} }
} }
/**
* Modify the AST to create a new sketch using the variable declaration
* of an offset plane. The new sketch just has to come after the offset
* plane declaration.
*/
export function sketchOnOffsetPlane(
node: Node<Program>,
offsetPathToNode: PathToNode
) {
let _node = { ...node }
// Find the offset plane declaration
const offsetPlaneDeclarator = getNodeFromPath<VariableDeclarator>(
_node,
offsetPathToNode,
'VariableDeclarator',
true
)
if (err(offsetPlaneDeclarator)) return offsetPlaneDeclarator
const { node: offsetPlaneNode } = offsetPlaneDeclarator
const offsetPlaneName = offsetPlaneNode.id.name
// Create a new sketch declaration
const newSketchName = findUniqueName(
node,
KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH
)
const newSketch = createVariableDeclaration(
newSketchName,
createCallExpressionStdLib('startSketchOn', [
createIdentifier(offsetPlaneName),
]),
undefined,
'const'
)
// Decide where to insert the new sketch declaration
const offsetIndex = offsetPathToNode[1][0]
if (!isPathToNodeNumber(offsetIndex)) {
return new Error('Expected offsetIndex to be a number')
}
// and insert it
_node.body.splice(offsetIndex + 1, 0, newSketch)
const newPathToNode = structuredClone(offsetPathToNode)
newPathToNode[1][0] = offsetIndex + 1
// Return the modified AST and the path to the new sketch declaration
return {
modifiedAst: _node,
pathToNode: newPathToNode,
}
}
export const getLastIndex = (pathToNode: PathToNode): number => export const getLastIndex = (pathToNode: PathToNode): number =>
splitPathAtLastIndex(pathToNode).index splitPathAtLastIndex(pathToNode).index
@ -629,7 +573,7 @@ export function splitPathAtPipeExpression(pathToNode: PathToNode): {
return splitPathAtPipeExpression(pathToNode.slice(0, -1)) return splitPathAtPipeExpression(pathToNode.slice(0, -1))
} }
export function createLiteral(value: LiteralValue): Node<Literal> { export function createLiteral(value: string | number): Node<Literal> {
return { return {
type: 'Literal', type: 'Literal',
start: 0, start: 0,

View File

@ -77,30 +77,22 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
code.indexOf(expectedExtrudeSnippet), code.indexOf(expectedExtrudeSnippet),
code.indexOf(expectedExtrudeSnippet) + expectedExtrudeSnippet.length, code.indexOf(expectedExtrudeSnippet) + expectedExtrudeSnippet.length,
] ]
const expectedExtrudePath = getNodePathFromSourceRange(ast, extrudeRange) const expedtedExtrudePath = getNodePathFromSourceRange(ast, extrudeRange)
const expectedExtrudeNodeResult = getNodeFromPath< const expedtedExtrudeNodeResult = getNodeFromPath<VariableDeclarator>(
VariableDeclarator | CallExpression ast,
>(ast, expectedExtrudePath) expedtedExtrudePath
if (err(expectedExtrudeNodeResult)) { )
return expectedExtrudeNodeResult if (err(expedtedExtrudeNodeResult)) {
return expedtedExtrudeNodeResult
} }
const expectedExtrudeNode = expectedExtrudeNodeResult.node const expectedExtrudeNode = expedtedExtrudeNodeResult.node
const init = expectedExtrudeNode.init
// check whether extrude is in the sketch pipe if (init.type !== 'CallExpression' && init.type !== 'PipeExpression') {
const extrudeInSketchPipe = expectedExtrudeNode.type === 'CallExpression' return new Error(
if (extrudeInSketchPipe) { 'Expected extrude expression is not a CallExpression or PipeExpression'
return expectedExtrudeNode )
} }
if (!extrudeInSketchPipe) { return init
const init = expectedExtrudeNode.init
if (init.type !== 'CallExpression' && init.type !== 'PipeExpression') {
return new Error(
'Expected extrude expression is not a CallExpression or PipeExpression'
)
}
return init
}
return new Error('Expected extrude expression not found')
} }
// ast // ast
@ -168,23 +160,6 @@ extrude001 = extrude(-15, sketch001)`
expectedExtrudeSnippet expectedExtrudeSnippet
) )
}, 5_000) }, 5_000)
it('should return the correct paths when extrusion occurs within the sketch pipe', async () => {
const code = `sketch001 = startSketchOn('XY')
|> startProfileAt([-10, 10], %)
|> line([20, 0], %)
|> line([0, -20], %)
|> line([-20, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> extrude(15, %)`
const selectedSegmentSnippet = `line([20, 0], %)`
const expectedExtrudeSnippet = `extrude(15, %)`
await runGetPathToExtrudeForSegmentSelectionTest(
code,
selectedSegmentSnippet,
expectedExtrudeSnippet
)
}, 5_000)
it('should return the correct paths for a valid selection and extrusion in case of several extrusions and sketches', async () => { it('should return the correct paths for a valid selection and extrusion in case of several extrusions and sketches', async () => {
const code = `sketch001 = startSketchOn('XY') const code = `sketch001 = startSketchOn('XY')
|> startProfileAt([-30, 30], %) |> startProfileAt([-30, 30], %)
@ -321,34 +296,6 @@ extrude001 = extrude(-15, sketch001)`
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%) |> close(%)
extrude001 = extrude(-15, sketch001) extrude001 = extrude(-15, sketch001)
|> fillet({ radius: 3, tags: [seg01] }, %)`
await runModifyAstCloneWithFilletAndTag(
code,
segmentSnippets,
radiusValue,
expectedCode
)
})
it('should add a fillet to the sketch pipe', async () => {
const code = `sketch001 = startSketchOn('XY')
|> startProfileAt([-10, 10], %)
|> line([20, 0], %)
|> line([0, -20], %)
|> line([-20, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> extrude(-15, %)`
const segmentSnippets = ['line([0, -20], %)']
const radiusValue = 3
const expectedCode = `sketch001 = startSketchOn('XY')
|> startProfileAt([-10, 10], %)
|> line([20, 0], %)
|> line([0, -20], %, $seg01)
|> line([-20, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> extrude(-15, %)
|> fillet({ radius: 3, tags: [seg01] }, %)` |> fillet({ radius: 3, tags: [seg01] }, %)`
await runModifyAstCloneWithFilletAndTag( await runModifyAstCloneWithFilletAndTag(

View File

@ -35,12 +35,7 @@ import {
ArtifactGraph, ArtifactGraph,
getSweepFromSuspectedPath, getSweepFromSuspectedPath,
} from 'lang/std/artifactGraph' } from 'lang/std/artifactGraph'
import { import { kclManager, engineCommandManager, editorManager } from 'lib/singletons'
kclManager,
engineCommandManager,
editorManager,
codeManager,
} from 'lib/singletons'
import { Node } from 'wasm-lib/kcl/bindings/Node' import { Node } from 'wasm-lib/kcl/bindings/Node'
// Apply Fillet To Selection // Apply Fillet To Selection
@ -146,7 +141,7 @@ export function modifyAstCloneWithFilletAndTag(
// Modify the extrude expression to include this fillet expression // Modify the extrude expression to include this fillet expression
// CallExpression - no fillet // CallExpression - no fillet
// PipeExpression - fillet exists or extrude in sketch pipe // PipeExpression - fillet exists
let pathToFilletNode: PathToNode = [] let pathToFilletNode: PathToNode = []
@ -167,7 +162,15 @@ export function modifyAstCloneWithFilletAndTag(
) )
pathToFilletNodes.push(pathToFilletNode) pathToFilletNodes.push(pathToFilletNode)
} else if (extrudeDeclarator.init.type === 'PipeExpression') { } else if (extrudeDeclarator.init.type === 'PipeExpression') {
// 2. case when fillet exists or extrude in sketch pipe // 2. case when fillet exists
const existingFilletCall = extrudeDeclarator.init.body.find((node) => {
return node.type === 'CallExpression' && node.callee.name === 'fillet'
})
if (!existingFilletCall || existingFilletCall.type !== 'CallExpression') {
return new Error('Fillet CallExpression not found.')
}
// mutate the extrude node with the new fillet call // mutate the extrude node with the new fillet call
extrudeDeclarator.init.body.push(filletCall) extrudeDeclarator.init.body.push(filletCall)
@ -250,9 +253,6 @@ async function updateAstAndFocus(
const updatedAst = await kclManager.updateAst(modifiedAst, true, { const updatedAst = await kclManager.updateAst(modifiedAst, true, {
focusPath: pathToFilletNode, focusPath: pathToFilletNode,
}) })
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
if (updatedAst?.selections) { if (updatedAst?.selections) {
editorManager.selectRange(updatedAst?.selections) editorManager.selectRange(updatedAst?.selections)
} }
@ -309,14 +309,14 @@ function locateExtrudeDeclarator(
node: Program, node: Program,
pathToExtrudeNode: PathToNode pathToExtrudeNode: PathToNode
): { extrudeDeclarator: VariableDeclarator } | Error { ): { extrudeDeclarator: VariableDeclarator } | Error {
const nodeOfExtrudeCall = getNodeFromPath<VariableDeclaration>( const extrudeChunk = getNodeFromPath<VariableDeclaration>(
node, node,
pathToExtrudeNode, pathToExtrudeNode,
'VariableDeclaration' 'VariableDeclaration'
) )
if (err(nodeOfExtrudeCall)) return nodeOfExtrudeCall if (err(extrudeChunk)) return extrudeChunk
const { node: extrudeVarDecl } = nodeOfExtrudeCall const { node: extrudeVarDecl } = extrudeChunk
const extrudeDeclarator = extrudeVarDecl.declarations[0] const extrudeDeclarator = extrudeVarDecl.declarations[0]
if (!extrudeDeclarator) { if (!extrudeDeclarator) {
return new Error('Extrude Declarator not found.') return new Error('Extrude Declarator not found.')

View File

@ -1,4 +1,4 @@
import { parse, recast, initPromise, PathToNode, Identifier } from './wasm' import { parse, recast, initPromise, PathToNode } from './wasm'
import { import {
findAllPreviousVariables, findAllPreviousVariables,
isNodeSafeToReplace, isNodeSafeToReplace,
@ -10,7 +10,6 @@ import {
hasSketchPipeBeenExtruded, hasSketchPipeBeenExtruded,
doesSceneHaveSweepableSketch, doesSceneHaveSweepableSketch,
traverse, traverse,
getNodeFromPath,
} from './queryAst' } from './queryAst'
import { enginelessExecutor } from '../lib/testHelpers' import { enginelessExecutor } from '../lib/testHelpers'
import { import {
@ -267,86 +266,6 @@ describe('testing getNodePathFromSourceRange', () => {
]) ])
expect(selectWholeThing).toEqual(expected) expect(selectWholeThing).toEqual(expected)
}) })
it('finds the node in if-else condition', () => {
const code = `y = 0
x = if x > y {
x + 1
} else {
y
}`
const searchLn = `x > y`
const sourceIndex = code.indexOf(searchLn)
const ast = parse(code)
if (err(ast)) throw ast
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
expect(result).toEqual([
['body', ''],
[1, 'index'],
['declarations', 'VariableDeclaration'],
[0, 'index'],
['init', ''],
['cond', 'IfExpression'],
['left', 'BinaryExpression'],
])
const _node = getNodeFromPath<Identifier>(ast, result)
if (err(_node)) throw _node
expect(_node.node.type).toEqual('Identifier')
expect(_node.node.name).toEqual('x')
})
it('finds the node in if-else then', () => {
const code = `y = 0
x = if x > y {
x + 1
} else {
y
}`
const searchLn = `x + 1`
const sourceIndex = code.indexOf(searchLn)
const ast = parse(code)
if (err(ast)) throw ast
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
expect(result).toEqual([
['body', ''],
[1, 'index'],
['declarations', 'VariableDeclaration'],
[0, 'index'],
['init', ''],
['then_val', 'IfExpression'],
['body', 'IfExpression'],
[0, 'index'],
['expression', 'ExpressionStatement'],
['left', 'BinaryExpression'],
])
const _node = getNodeFromPath<Identifier>(ast, result)
if (err(_node)) throw _node
expect(_node.node.type).toEqual('Identifier')
expect(_node.node.name).toEqual('x')
})
it('finds the node in import statement item', () => {
const code = `import foo, bar as baz from 'thing.kcl'`
const searchLn = `bar`
const sourceIndex = code.indexOf(searchLn)
const ast = parse(code)
if (err(ast)) throw ast
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
expect(result).toEqual([
['body', ''],
[0, 'index'],
['items', 'ImportStatement'],
[1, 'index'],
['name', 'ImportItem'],
])
const _node = getNodeFromPath<Identifier>(ast, result)
if (err(_node)) throw _node
expect(_node.node.type).toEqual('Identifier')
expect(_node.node.name).toEqual('bar')
})
}) })
describe('testing doesPipeHave', () => { describe('testing doesPipeHave', () => {
@ -530,25 +449,14 @@ describe('Testing hasSketchPipeBeenExtruded', () => {
|> line([-17.67, 0.85], %) |> line([-17.67, 0.85], %)
|> close(%) |> close(%)
extrude001 = extrude(10, sketch001) extrude001 = extrude(10, sketch001)
sketch002 = startSketchOn(extrude001, seg01) sketch002 = startSketchOn(extrude001, $seg01)
|> startProfileAt([-12.94, 6.6], %) |> startProfileAt([-12.94, 6.6], %)
|> line([2.45, -0.2], %) |> line([2.45, -0.2], %)
|> line([-2, -1.25], %) |> line([-2, -1.25], %)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%) |> close(%)
sketch003 = startSketchOn(extrude001, 'END')
|> startProfileAt([8.14, 2.8], %)
|> line([-1.24, 4.39], %)
|> line([3.79, 1.91], %)
|> line([1.77, -2.95], %)
|> line([3.12, 1.74], %)
|> line([1.91, -4.09], %)
|> line([-5.6, -2.75], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> extrude(3.14, %)
` `
it('identifies sketch001 pipe as extruded (extrusion after pipe)', async () => { it('finds sketch001 pipe to be extruded', async () => {
const ast = parse(exampleCode) const ast = parse(exampleCode)
if (err(ast)) throw ast if (err(ast)) throw ast
const lineOfInterest = `line([4.99, -0.46], %, $seg01)` const lineOfInterest = `line([4.99, -0.46], %, $seg01)`
@ -563,7 +471,7 @@ sketch003 = startSketchOn(extrude001, 'END')
) )
expect(extruded).toBeTruthy() expect(extruded).toBeTruthy()
}) })
it('identifies sketch002 pipe as not extruded', async () => { it('find sketch002 NOT pipe to be extruded', async () => {
const ast = parse(exampleCode) const ast = parse(exampleCode)
if (err(ast)) throw ast if (err(ast)) throw ast
const lineOfInterest = `line([2.45, -0.2], %)` const lineOfInterest = `line([2.45, -0.2], %)`
@ -578,21 +486,6 @@ sketch003 = startSketchOn(extrude001, 'END')
) )
expect(extruded).toBeFalsy() expect(extruded).toBeFalsy()
}) })
it('identifies sketch003 pipe as extruded (extrusion within pipe)', async () => {
const ast = parse(exampleCode)
if (err(ast)) throw ast
const lineOfInterest = `|> line([3.12, 1.74], %)`
const characterIndex =
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
const extruded = hasSketchPipeBeenExtruded(
{
range: [characterIndex, characterIndex],
type: 'default',
},
ast
)
expect(extruded).toBeTruthy()
})
}) })
describe('Testing doesSceneHaveSweepableSketch', () => { describe('Testing doesSceneHaveSweepableSketch', () => {

View File

@ -14,7 +14,6 @@ import {
ProgramMemory, ProgramMemory,
ReturnStatement, ReturnStatement,
sketchFromKclValue, sketchFromKclValue,
sketchFromKclValueOptional,
SourceRange, SourceRange,
SyntaxType, SyntaxType,
VariableDeclaration, VariableDeclaration,
@ -28,7 +27,7 @@ import {
getConstraintLevelFromSourceRange, getConstraintLevelFromSourceRange,
getConstraintType, getConstraintType,
} from './std/sketchcombos' } from './std/sketchcombos'
import { err, Reason } from 'lib/trap' import { err } from 'lib/trap'
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement' import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
import { Node } from 'wasm-lib/kcl/bindings/Node' import { Node } from 'wasm-lib/kcl/bindings/Node'
@ -318,62 +317,6 @@ function moreNodePathFromSourceRange(
} }
if (_node.type === 'PipeSubstitution' && isInRange) return path if (_node.type === 'PipeSubstitution' && isInRange) return path
if (_node.type === 'IfExpression' && isInRange) {
const { cond, then_val, else_ifs, final_else } = _node
if (cond.start <= start && cond.end >= end) {
path.push(['cond', 'IfExpression'])
return moreNodePathFromSourceRange(cond, sourceRange, path)
}
if (then_val.start <= start && then_val.end >= end) {
path.push(['then_val', 'IfExpression'])
path.push(['body', 'IfExpression'])
return getNodePathFromSourceRange(then_val, sourceRange, path)
}
for (let i = 0; i < else_ifs.length; i++) {
const else_if = else_ifs[i]
if (else_if.start <= start && else_if.end >= end) {
path.push(['else_ifs', 'IfExpression'])
path.push([i, 'index'])
const { cond, then_val } = else_if
if (cond.start <= start && cond.end >= end) {
path.push(['cond', 'IfExpression'])
return moreNodePathFromSourceRange(cond, sourceRange, path)
}
path.push(['then_val', 'IfExpression'])
path.push(['body', 'IfExpression'])
return getNodePathFromSourceRange(then_val, sourceRange, path)
}
}
if (final_else.start <= start && final_else.end >= end) {
path.push(['final_else', 'IfExpression'])
path.push(['body', 'IfExpression'])
return getNodePathFromSourceRange(final_else, sourceRange, path)
}
return path
}
if (_node.type === 'ImportStatement' && isInRange) {
const { items } = _node
for (let i = 0; i < items.length; i++) {
const item = items[i]
if (item.start <= start && item.end >= end) {
path.push(['items', 'ImportStatement'])
path.push([i, 'index'])
if (item.name.start <= start && item.name.end >= end) {
path.push(['name', 'ImportItem'])
return path
}
if (item.alias && item.alias.start <= start && item.alias.end >= end) {
path.push(['alias', 'ImportItem'])
return path
}
return path
}
}
return path
}
console.error('not implemented: ' + node.type) console.error('not implemented: ' + node.type)
return path return path
@ -847,8 +790,7 @@ export function hasExtrudeSketch({
const varName = varDec.declarations[0].id.name const varName = varDec.declarations[0].id.name
const varValue = programMemory?.get(varName) const varValue = programMemory?.get(varName)
return ( return (
varValue?.type === 'Solid' || varValue?.type === 'Solid' || !err(sketchFromKclValue(varValue, varName))
!(sketchFromKclValueOptional(varValue, varName) instanceof Reason)
) )
} }
@ -929,11 +871,7 @@ export function findUsesOfTagInPipe(
export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) { export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
const path = getNodePathFromSourceRange(ast, selection.range) const path = getNodePathFromSourceRange(ast, selection.range)
const _node = getNodeFromPath<Node<PipeExpression>>( const _node = getNodeFromPath<PipeExpression>(ast, path, 'PipeExpression')
ast,
path,
'PipeExpression'
)
if (err(_node)) return false if (err(_node)) return false
const { node: pipeExpression } = _node const { node: pipeExpression } = _node
if (pipeExpression.type !== 'PipeExpression') return false if (pipeExpression.type !== 'PipeExpression') return false
@ -946,33 +884,19 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
const varDec = _varDec.node const varDec = _varDec.node
if (varDec.type !== 'VariableDeclarator') return false if (varDec.type !== 'VariableDeclarator') return false
let extruded = false let extruded = false
// option 1: extrude or revolve is called in the sketch pipe traverse(ast as any, {
traverse(pipeExpression, {
enter(node) { enter(node) {
if ( if (
node.type === 'CallExpression' && node.type === 'CallExpression' &&
(node.callee.name === 'extrude' || node.callee.name === 'revolve') node.callee.type === 'Identifier' &&
(node.callee.name === 'extrude' || node.callee.name === 'revolve') &&
node.arguments?.[1]?.type === 'Identifier' &&
node.arguments[1].name === varDec.id.name
) { ) {
extruded = true extruded = true
} }
}, },
}) })
// option 2: extrude or revolve is called in the separate pipe
if (!extruded) {
traverse(ast as any, {
enter(node) {
if (
node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
(node.callee.name === 'extrude' || node.callee.name === 'revolve') &&
node.arguments?.[1]?.type === 'Identifier' &&
node.arguments[1].name === varDec.id.name
) {
extruded = true
}
},
})
}
return extruded return extruded
} }

View File

@ -98,22 +98,12 @@ sketch004 = startSketchOn(extrude003, seg02)
|> close(%) |> close(%)
extrude004 = extrude(3, sketch004) extrude004 = extrude(3, sketch004)
` `
const exampleCodeOffsetPlanes = `
offsetPlane001 = offsetPlane("XY", 20)
offsetPlane002 = offsetPlane("XZ", -50)
offsetPlane003 = offsetPlane("YZ", 10)
sketch002 = startSketchOn(offsetPlane001)
|> startProfileAt([0, 0], %)
|> line([6.78, 15.01], %)
`
// add more code snippets here and use `getCommands` to get the orderedCommands and responseMap for more tests // add more code snippets here and use `getCommands` to get the orderedCommands and responseMap for more tests
const codeToWriteCacheFor = { const codeToWriteCacheFor = {
exampleCode1, exampleCode1,
sketchOnFaceOnFaceEtc, sketchOnFaceOnFaceEtc,
exampleCodeNo3D, exampleCodeNo3D,
exampleCodeOffsetPlanes,
} as const } as const
type CodeKey = keyof typeof codeToWriteCacheFor type CodeKey = keyof typeof codeToWriteCacheFor
@ -175,52 +165,6 @@ afterAll(() => {
}) })
describe('testing createArtifactGraph', () => { describe('testing createArtifactGraph', () => {
describe('code with offset planes and a sketch:', () => {
let ast: Program
let theMap: ReturnType<typeof createArtifactGraph>
it('setup', () => {
// putting this logic in here because describe blocks runs before beforeAll has finished
const {
orderedCommands,
responseMap,
ast: _ast,
} = getCommands('exampleCodeOffsetPlanes')
ast = _ast
theMap = createArtifactGraph({ orderedCommands, responseMap, ast })
})
it(`there should be one sketch`, () => {
const sketches = [...filterArtifacts({ types: ['path'] }, theMap)].map(
(path) => expandPath(path[1], theMap)
)
expect(sketches).toHaveLength(1)
sketches.forEach((path) => {
if (err(path)) throw path
expect(path.type).toBe('path')
})
})
it(`there should be three offsetPlanes`, () => {
const offsetPlanes = [
...filterArtifacts({ types: ['plane'] }, theMap),
].map((plane) => expandPlane(plane[1], theMap))
expect(offsetPlanes).toHaveLength(3)
offsetPlanes.forEach((path) => {
expect(path.type).toBe('plane')
})
})
it(`Only one offset plane should have a path`, () => {
const offsetPlanes = [
...filterArtifacts({ types: ['plane'] }, theMap),
].map((plane) => expandPlane(plane[1], theMap))
const offsetPlaneWithPaths = offsetPlanes.filter(
(plane) => plane.paths.length
)
expect(offsetPlaneWithPaths).toHaveLength(1)
})
})
describe('code with an extrusion, fillet and sketch of face:', () => { describe('code with an extrusion, fillet and sketch of face:', () => {
let ast: Program let ast: Program
let theMap: ReturnType<typeof createArtifactGraph> let theMap: ReturnType<typeof createArtifactGraph>

View File

@ -249,20 +249,7 @@ export function getArtifactsToUpdate({
const cmd = command.cmd const cmd = command.cmd
const returnArr: ReturnType<typeof getArtifactsToUpdate> = [] const returnArr: ReturnType<typeof getArtifactsToUpdate> = []
if (!response) return returnArr if (!response) return returnArr
if (cmd.type === 'make_plane' && range[1] !== 0) { if (cmd.type === 'enable_sketch_mode') {
// If we're calling `make_plane` and the code range doesn't end at `0`
// it's not a default plane, but a custom one from the offsetPlane standard library function
return [
{
id,
artifact: {
type: 'plane',
pathIds: [],
codeRef: { range, pathToNode },
},
},
]
} else if (cmd.type === 'enable_sketch_mode') {
const plane = getArtifact(currentPlaneId) const plane = getArtifact(currentPlaneId)
const pathIds = plane?.type === 'plane' ? plane?.pathIds : [] const pathIds = plane?.type === 'plane' ? plane?.pathIds : []
const codeRef = const codeRef =
@ -646,7 +633,7 @@ export function expandSweep(
if (err(path)) return path if (err(path)) return path
return { return {
type: 'sweep', type: 'sweep',
subType: sweep.subType, subType: 'extrusion',
surfaces: Array.from(surfs.values()), surfaces: Array.from(surfs.values()),
edges: Array.from(edges.values()), edges: Array.from(edges.values()),
path, path,

View File

@ -1631,11 +1631,7 @@ export class EngineCommandManager extends EventTarget {
switch (this.exportInfo.intent) { switch (this.exportInfo.intent) {
case ExportIntent.Save: { case ExportIntent.Save: {
exportSave({ exportSave(event.data, this.pendingExport.toastId).then(() => {
data: event.data,
fileName: this.exportInfo.name,
toastId: this.pendingExport.toastId,
}).then(() => {
this.pendingExport?.resolve(null) this.pendingExport?.resolve(null)
}, this.pendingExport?.reject) }, this.pendingExport?.reject)
break break

View File

@ -1,12 +1,5 @@
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { import { Program, PathToNode } from './wasm'
Program,
PathToNode,
CallExpression,
Literal,
ArrayExpression,
BinaryExpression,
} from './wasm'
import { getNodeFromPath } from './queryAst' import { getNodeFromPath } from './queryAst'
import { ArtifactGraph, filterArtifacts } from 'lang/std/artifactGraph' import { ArtifactGraph, filterArtifacts } from 'lang/std/artifactGraph'
import { isOverlap } from 'lib/utils' import { isOverlap } from 'lib/utils'
@ -91,19 +84,3 @@ export function isCursorInSketchCommandRange(
([, artifact]) => artifact.type === 'path' ([, artifact]) => artifact.type === 'path'
)?.[0] || false )?.[0] || false
} }
export function isCallExpression(e: any): e is CallExpression {
return e && e.type === 'CallExpression'
}
export function isArrayExpression(e: any): e is ArrayExpression {
return e && e.type === 'ArrayExpression'
}
export function isLiteral(e: any): e is Literal {
return e && e.type === 'Literal'
}
export function isBinaryExpression(e: any): e is BinaryExpression {
return e && e.type === 'BinaryExpression'
}

View File

@ -32,7 +32,7 @@ import { CoreDumpManager } from 'lib/coredump'
import openWindow from 'lib/openWindow' import openWindow from 'lib/openWindow'
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes' import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
import { TEST } from 'env' import { TEST } from 'env'
import { err, Reason } from 'lib/trap' import { err } from 'lib/trap'
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration' import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
import { DeepPartial } from 'lib/types' import { DeepPartial } from 'lib/types'
import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration' import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration'
@ -62,7 +62,6 @@ export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator' export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart' export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
export type { Literal } from '../wasm-lib/kcl/bindings/Literal' export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
export type { LiteralValue } from '../wasm-lib/kcl/bindings/LiteralValue'
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression' export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
export type SyntaxType = export type SyntaxType =
@ -82,7 +81,6 @@ export type SyntaxType =
| 'PipeExpression' | 'PipeExpression'
| 'PipeSubstitution' | 'PipeSubstitution'
| 'Literal' | 'Literal'
| 'LiteralValue'
| 'NonCodeNode' | 'NonCodeNode'
| 'UnaryExpression' | 'UnaryExpression'
@ -144,12 +142,6 @@ export const parse = (code: string | Error): Node<Program> | Error => {
export type PathToNode = [string | number, string][] export type PathToNode = [string | number, string][]
export const isPathToNodeNumber = (
pathToNode: string | number
): pathToNode is number => {
return typeof pathToNode === 'number'
}
export interface ExecState { export interface ExecState {
memory: ProgramMemory memory: ProgramMemory
idGenerator: IdGenerator idGenerator: IdGenerator
@ -344,7 +336,7 @@ export class ProgramMemory {
*/ */
hasSketchOrSolid(): boolean { hasSketchOrSolid(): boolean {
for (const node of this.visibleEntries().values()) { for (const node of this.visibleEntries().values()) {
if (node.type === 'Solid' || node.type === 'Sketch') { if (node.type === 'Solid' || node.value?.type === 'Sketch') {
return true return true
} }
} }
@ -365,10 +357,10 @@ export class ProgramMemory {
} }
// TODO: In the future, make the parameter be a KclValue. // TODO: In the future, make the parameter be a KclValue.
export function sketchFromKclValueOptional( export function sketchFromKclValue(
obj: any, obj: any,
varName: string | null varName: string | null
): Sketch | Reason { ): Sketch | Error {
if (obj?.value?.type === 'Sketch') return obj.value if (obj?.value?.type === 'Sketch') return obj.value
if (obj?.value?.type === 'Solid') return obj.value.sketch if (obj?.value?.type === 'Solid') return obj.value.sketch
if (obj?.type === 'Solid') return obj.sketch if (obj?.type === 'Solid') return obj.sketch
@ -377,26 +369,15 @@ export function sketchFromKclValueOptional(
} }
const actualType = obj?.value?.type ?? obj?.type const actualType = obj?.value?.type ?? obj?.type
if (actualType) { if (actualType) {
return new Reason( console.log(obj)
return new Error(
`Expected ${varName} to be a sketch or solid, but it was ${actualType} instead.` `Expected ${varName} to be a sketch or solid, but it was ${actualType} instead.`
) )
} else { } else {
return new Reason(`Expected ${varName} to be a sketch, but it wasn't.`) return new Error(`Expected ${varName} to be a sketch, but it wasn't.`)
} }
} }
// TODO: In the future, make the parameter be a KclValue.
export function sketchFromKclValue(
obj: any,
varName: string | null
): Sketch | Error {
const result = sketchFromKclValueOptional(obj, varName)
if (result instanceof Reason) {
return result.toError()
}
return result
}
export const executor = async ( export const executor = async (
node: Node<Program>, node: Node<Program>,
programMemory: ProgramMemory | Error = ProgramMemory.empty(), programMemory: ProgramMemory | Error = ProgramMemory.empty(),

View File

@ -68,16 +68,7 @@ const save_ = async (file: ModelingAppFile, toastId: string) => {
} }
// Saves files locally from an export call. // Saves files locally from an export call.
// We override the file's name with one passed in from the client side. export async function exportSave(data: ArrayBuffer, toastId: string) {
export async function exportSave({
data,
fileName,
toastId,
}: {
data: ArrayBuffer
fileName: string
toastId: string
}) {
// This converts the ArrayBuffer to a Rust equivalent Vec<u8>. // This converts the ArrayBuffer to a Rust equivalent Vec<u8>.
let uintArray = new Uint8Array(data) let uintArray = new Uint8Array(data)
@ -89,10 +80,9 @@ export async function exportSave({
zip.file(file.name, new Uint8Array(file.contents), { binary: true }) zip.file(file.name, new Uint8Array(file.contents), { binary: true })
} }
return zip.generateAsync({ type: 'array' }).then((contents) => { return zip.generateAsync({ type: 'array' }).then((contents) => {
return save_({ name: `${fileName || 'output'}.zip`, contents }, toastId) return save_({ name: 'output.zip', contents }, toastId)
}) })
} else { } else {
files[0].name = fileName || files[0].name
return save_(files[0], toastId) return save_(files[0], toastId)
} }
} }

View File

@ -9,16 +9,8 @@ import {
createUnaryExpression, createUnaryExpression,
} from 'lang/modifyAst' } from 'lang/modifyAst'
import { ArrayExpression, CallExpression, PipeExpression } from 'lang/wasm' import { ArrayExpression, CallExpression, PipeExpression } from 'lang/wasm'
import { roundOff } from 'lib/utils'
import {
isCallExpression,
isArrayExpression,
isLiteral,
isBinaryExpression,
} from 'lang/util'
/** /**
* It does not create the startSketchOn and it does not create the startProfileAt.
* Returns AST expressions for this KCL code: * Returns AST expressions for this KCL code:
* const yo = startSketchOn('XY') * const yo = startSketchOn('XY')
* |> startProfileAt([0, 0], %) * |> startProfileAt([0, 0], %)
@ -100,69 +92,3 @@ export function updateRectangleSketch(
createLiteral(Math.abs(y)), // This will be the height of the rectangle createLiteral(Math.abs(y)), // This will be the height of the rectangle
]) ])
} }
/**
* Mutates the pipeExpression to update the center rectangle sketch
* @param pipeExpression
* @param x
* @param y
* @param tag
*/
export function updateCenterRectangleSketch(
pipeExpression: PipeExpression,
deltaX: number,
deltaY: number,
tag: string,
originX: number,
originY: number
) {
let startX = originX - Math.abs(deltaX)
let startY = originY - Math.abs(deltaY)
// pipeExpression.body[1] is startProfileAt
let callExpression = pipeExpression.body[1]
if (isCallExpression(callExpression)) {
const arrayExpression = callExpression.arguments[0]
if (isArrayExpression(arrayExpression)) {
callExpression.arguments[0] = createArrayExpression([
createLiteral(roundOff(startX)),
createLiteral(roundOff(startY)),
])
}
}
const twoX = deltaX * 2
const twoY = deltaY * 2
callExpression = pipeExpression.body[2]
if (isCallExpression(callExpression)) {
const arrayExpression = callExpression.arguments[0]
if (isArrayExpression(arrayExpression)) {
const literal = arrayExpression.elements[0]
if (isLiteral(literal)) {
callExpression.arguments[0] = createArrayExpression([
createLiteral(literal.value),
createLiteral(Math.abs(twoX)),
])
}
}
}
callExpression = pipeExpression.body[3]
if (isCallExpression(callExpression)) {
const arrayExpression = callExpression.arguments[0]
if (isArrayExpression(arrayExpression)) {
const binaryExpression = arrayExpression.elements[0]
if (isBinaryExpression(binaryExpression)) {
callExpression.arguments[0] = createArrayExpression([
createBinaryExpression([
createCallExpressionStdLib('segAng', [createIdentifier(tag)]),
binaryExpression.operator,
createLiteral(90),
]), // 90 offset from the previous line
createLiteral(Math.abs(twoY)), // This will be the height of the rectangle
])
}
}
}
}

View File

@ -124,9 +124,7 @@ export const fileLoader: LoaderFunction = async (
// We explicitly do not write to the file here since we are loading from // We explicitly do not write to the file here since we are loading from
// the file system and not the editor. // the file system and not the editor.
codeManager.updateCurrentFilePath(currentFilePath) codeManager.updateCurrentFilePath(currentFilePath)
// We pass true on the end here to clear the code editor history. codeManager.updateCodeStateEditor(code)
// This way undo and redo are not super weird when opening new files.
codeManager.updateCodeStateEditor(code, true)
} }
// Set the file system manager to the project path // Set the file system manager to the project path

View File

@ -407,9 +407,8 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
status: 'available', status: 'available',
title: 'Center circle', title: 'Center circle',
disabled: (state) => disabled: (state) =>
state.matches('Sketch no face') || !canRectangleOrCircleTool(state.context) &&
(!canRectangleOrCircleTool(state.context) && !state.matches({ Sketch: 'Circle tool' }),
!state.matches({ Sketch: 'Circle tool' })),
isActive: (state) => state.matches({ Sketch: 'Circle tool' }), isActive: (state) => state.matches({ Sketch: 'Circle tool' }),
hotkey: (state) => hotkey: (state) =>
state.matches({ Sketch: 'Circle tool' }) ? ['Esc', 'C'] : 'C', state.matches({ Sketch: 'Circle tool' }) ? ['Esc', 'C'] : 'C',
@ -449,9 +448,8 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
icon: 'rectangle', icon: 'rectangle',
status: 'available', status: 'available',
disabled: (state) => disabled: (state) =>
state.matches('Sketch no face') || !canRectangleOrCircleTool(state.context) &&
(!canRectangleOrCircleTool(state.context) && !state.matches({ Sketch: 'Rectangle tool' }),
!state.matches({ Sketch: 'Rectangle tool' })),
title: 'Corner rectangle', title: 'Corner rectangle',
hotkey: (state) => hotkey: (state) =>
state.matches({ Sketch: 'Rectangle tool' }) ? ['Esc', 'R'] : 'R', state.matches({ Sketch: 'Rectangle tool' }) ? ['Esc', 'R'] : 'R',
@ -461,33 +459,13 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
}, },
{ {
id: 'center-rectangle', id: 'center-rectangle',
onClick: ({ modelingState, modelingSend }) => onClick: () => console.error('Center rectangle not yet implemented'),
modelingSend({ icon: 'rectangle',
type: 'change tool', status: 'unavailable',
data: {
tool: !modelingState.matches({
Sketch: 'Center Rectangle tool',
})
? 'center rectangle'
: 'none',
},
}),
icon: 'arc',
status: 'available',
disabled: (state) =>
state.matches('Sketch no face') ||
(!canRectangleOrCircleTool(state.context) &&
!state.matches({ Sketch: 'Center Rectangle tool' })),
title: 'Center rectangle', title: 'Center rectangle',
hotkey: (state) => showTitle: false,
state.matches({ Sketch: 'Center Rectangle tool' })
? ['Esc', 'C']
: 'C',
description: 'Start drawing a rectangle from its center', description: 'Start drawing a rectangle from its center',
links: [], links: [],
isActive: (state) => {
return state.matches({ Sketch: 'Center Rectangle tool' })
},
}, },
], ],
{ {

View File

@ -2,23 +2,6 @@ import toast from 'react-hot-toast'
type ExcludeErr<T> = Exclude<T, Error> type ExcludeErr<T> = Exclude<T, Error>
/**
* Similar to Error, but more lightweight, without the stack trace. It can also
* be used to represent a reason for not being able to provide an alternative,
* which isn't necessarily an error.
*/
export class Reason {
message: string
constructor(message: string) {
this.message = message
}
toError() {
return new Error(this.message)
}
}
/** /**
* This is intentionally *not* exported due to misuse. We'd like to add a lint. * This is intentionally *not* exported due to misuse. We'd like to add a lint.
*/ */

View File

@ -91,7 +91,7 @@ export function useCalculateKclExpression({
const _programMem: ProgramMemory = ProgramMemory.empty() const _programMem: ProgramMemory = ProgramMemory.empty()
for (const { key, value } of availableVarInfo.variables) { for (const { key, value } of availableVarInfo.variables) {
const error = _programMem.set(key, { const error = _programMem.set(key, {
type: 'String', type: 'UserVal',
value, value,
__meta: [], __meta: [],
}) })
@ -115,7 +115,6 @@ export function useCalculateKclExpression({
setCalcResult(typeof result === 'number' ? String(result) : 'NAN') setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
init && setValueNode(init) init && setValueNode(init)
} }
if (!value) return
execAstAndSetResult().catch(() => { execAstAndSetResult().catch(() => {
setCalcResult('NAN') setCalcResult('NAN')
setValueNode(null) setValueNode(null)

View File

@ -18,7 +18,6 @@ import {
sceneEntitiesManager, sceneEntitiesManager,
engineCommandManager, engineCommandManager,
editorManager, editorManager,
codeManager,
} from 'lib/singletons' } from 'lib/singletons'
import { import {
horzVertInfo, horzVertInfo,
@ -159,15 +158,6 @@ export type DefaultPlane = {
yAxis: [number, number, number] yAxis: [number, number, number]
} }
export type OffsetPlane = {
type: 'offsetPlane'
position: [number, number, number]
planeId: string
pathToNode: PathToNode
zAxis: [number, number, number]
yAxis: [number, number, number]
}
export type SegmentOverlayPayload = export type SegmentOverlayPayload =
| { | {
type: 'set-one' type: 'set-one'
@ -193,7 +183,6 @@ export type SketchTool =
| 'line' | 'line'
| 'tangentialArc' | 'tangentialArc'
| 'rectangle' | 'rectangle'
| 'center rectangle'
| 'circle' | 'circle'
| 'none' | 'none'
@ -207,7 +196,7 @@ export type ModelingMachineEvent =
| { type: 'Sketch On Face' } | { type: 'Sketch On Face' }
| { | {
type: 'Select default plane' type: 'Select default plane'
data: DefaultPlane | ExtrudeFacePlane | OffsetPlane data: DefaultPlane | ExtrudeFacePlane
} }
| { | {
type: 'Set selection' type: 'Set selection'
@ -248,10 +237,6 @@ export type ModelingMachineEvent =
type: 'Add rectangle origin' type: 'Add rectangle origin'
data: [x: number, y: number] data: [x: number, y: number]
} }
| {
type: 'Add center rectangle origin'
data: [x: number, y: number]
}
| { | {
type: 'Add circle origin' type: 'Add circle origin'
data: [x: number, y: number] data: [x: number, y: number]
@ -292,7 +277,6 @@ export type ModelingMachineEvent =
} }
} }
| { type: 'Finish rectangle' } | { type: 'Finish rectangle' }
| { type: 'Finish center rectangle' }
| { type: 'Finish circle' } | { type: 'Finish circle' }
| { type: 'Artifact graph populated' } | { type: 'Artifact graph populated' }
| { type: 'Artifact graph emptied' } | { type: 'Artifact graph emptied' }
@ -521,9 +505,6 @@ export const modelingMachine = setup({
'next is rectangle': ({ context: { sketchDetails, currentTool } }) => 'next is rectangle': ({ context: { sketchDetails, currentTool } }) =>
currentTool === 'rectangle' && currentTool === 'rectangle' &&
canRectangleOrCircleTool({ sketchDetails }), canRectangleOrCircleTool({ sketchDetails }),
'next is center rectangle': ({ context: { sketchDetails, currentTool } }) =>
currentTool === 'center rectangle' &&
canRectangleOrCircleTool({ sketchDetails }),
'next is circle': ({ context: { sketchDetails, currentTool } }) => 'next is circle': ({ context: { sketchDetails, currentTool } }) =>
currentTool === 'circle' && canRectangleOrCircleTool({ sketchDetails }), currentTool === 'circle' && canRectangleOrCircleTool({ sketchDetails }),
'next is line': ({ context }) => context.currentTool === 'line', 'next is line': ({ context }) => context.currentTool === 'line',
@ -550,10 +531,8 @@ export const modelingMachine = setup({
} }
} }
), ),
'hide default planes': () => { // eslint-disable-next-line @typescript-eslint/no-misused-promises
// eslint-disable-next-line @typescript-eslint/no-floating-promises 'hide default planes': () => kclManager.hidePlanes(),
kclManager.hidePlanes()
},
'reset sketch metadata': assign({ 'reset sketch metadata': assign({
sketchDetails: null, sketchDetails: null,
sketchEnginePathId: '', sketchEnginePathId: '',
@ -616,6 +595,7 @@ export const modelingMachine = setup({
if (trap(extrudeSketchRes)) return if (trap(extrudeSketchRes)) return
const { modifiedAst, pathToExtrudeArg } = extrudeSketchRes const { modifiedAst, pathToExtrudeArg } = extrudeSketchRes
store.videoElement?.pause()
const updatedAst = await kclManager.updateAst(modifiedAst, true, { const updatedAst = await kclManager.updateAst(modifiedAst, true, {
focusPath: [pathToExtrudeArg], focusPath: [pathToExtrudeArg],
zoomToFit: true, zoomToFit: true,
@ -624,9 +604,11 @@ export const modelingMachine = setup({
type: 'path', type: 'path',
}, },
}) })
if (!engineCommandManager.engineConnection?.idleMode) {
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst) store.videoElement?.play().catch((e) => {
console.warn('Video playing was prevented', e)
})
}
if (updatedAst?.selections) { if (updatedAst?.selections) {
editorManager.selectRange(updatedAst?.selections) editorManager.selectRange(updatedAst?.selections)
} }
@ -660,6 +642,7 @@ export const modelingMachine = setup({
if (trap(revolveSketchRes)) return if (trap(revolveSketchRes)) return
const { modifiedAst, pathToRevolveArg } = revolveSketchRes const { modifiedAst, pathToRevolveArg } = revolveSketchRes
store.videoElement?.pause()
const updatedAst = await kclManager.updateAst(modifiedAst, true, { const updatedAst = await kclManager.updateAst(modifiedAst, true, {
focusPath: [pathToRevolveArg], focusPath: [pathToRevolveArg],
zoomToFit: true, zoomToFit: true,
@ -668,9 +651,11 @@ export const modelingMachine = setup({
type: 'path', type: 'path',
}, },
}) })
if (!engineCommandManager.engineConnection?.idleMode) {
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst) store.videoElement?.play().catch((e) => {
console.warn('Video playing was prevented', e)
})
}
if (updatedAst?.selections) { if (updatedAst?.selections) {
editorManager.selectRange(updatedAst?.selections) editorManager.selectRange(updatedAst?.selections)
} }
@ -700,7 +685,6 @@ export const modelingMachine = setup({
} }
await kclManager.updateAst(modifiedAst, true) await kclManager.updateAst(modifiedAst, true)
await codeManager.updateEditorWithAstAndWriteToFile(modifiedAst)
})().catch(reportRejection) })().catch(reportRejection)
}, },
'AST fillet': ({ event }) => { 'AST fillet': ({ event }) => {
@ -718,9 +702,6 @@ export const modelingMachine = setup({
radius radius
) )
if (err(applyFilletToSelectionResult)) return applyFilletToSelectionResult if (err(applyFilletToSelectionResult)) return applyFilletToSelectionResult
// eslint-disable-next-line @typescript-eslint/no-floating-promises
codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
}, },
'set selection filter to curves only': () => { 'set selection filter to curves only': () => {
;(async () => { ;(async () => {
@ -777,35 +758,25 @@ export const modelingMachine = setup({
'remove sketch grid': () => sceneEntitiesManager.removeSketchGrid(), 'remove sketch grid': () => sceneEntitiesManager.removeSketchGrid(),
'set up draft line': ({ context: { sketchDetails } }) => { 'set up draft line': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return if (!sketchDetails) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
sceneEntitiesManager sceneEntitiesManager.setUpDraftSegment(
.setupDraftSegment( sketchDetails.sketchPathToNode,
sketchDetails.sketchPathToNode, sketchDetails.zAxis,
sketchDetails.zAxis, sketchDetails.yAxis,
sketchDetails.yAxis, sketchDetails.origin,
sketchDetails.origin, 'line'
'line' )
)
.then(() => {
return codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
})
}, },
'set up draft arc': ({ context: { sketchDetails } }) => { 'set up draft arc': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return if (!sketchDetails) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
sceneEntitiesManager sceneEntitiesManager.setUpDraftSegment(
.setupDraftSegment( sketchDetails.sketchPathToNode,
sketchDetails.sketchPathToNode, sketchDetails.zAxis,
sketchDetails.zAxis, sketchDetails.yAxis,
sketchDetails.yAxis, sketchDetails.origin,
sketchDetails.origin, 'tangentialArcTo'
'tangentialArcTo' )
)
.then(() => {
return codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
})
}, },
'listen for rectangle origin': ({ context: { sketchDetails } }) => { 'listen for rectangle origin': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return if (!sketchDetails) return
@ -824,26 +795,6 @@ export const modelingMachine = setup({
}, },
}) })
}, },
'listen for center rectangle origin': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return
// setupNoPointsListener has the code for startProfileAt onClick
sceneEntitiesManager.setupNoPointsListener({
sketchDetails,
afterClick: (args) => {
const twoD = args.intersectionPoint?.twoD
if (twoD) {
sceneInfra.modelingSend({
type: 'Add center rectangle origin',
data: [twoD.x, twoD.y],
})
} else {
console.error('No intersection point found')
}
},
})
},
'listen for circle origin': ({ context: { sketchDetails } }) => { 'listen for circle origin': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return if (!sketchDetails) return
sceneEntitiesManager.createIntersectionPlane() sceneEntitiesManager.createIntersectionPlane()
@ -883,28 +834,8 @@ export const modelingMachine = setup({
'set up draft rectangle': ({ context: { sketchDetails }, event }) => { 'set up draft rectangle': ({ context: { sketchDetails }, event }) => {
if (event.type !== 'Add rectangle origin') return if (event.type !== 'Add rectangle origin') return
if (!sketchDetails || !event.data) return if (!sketchDetails || !event.data) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
sceneEntitiesManager sceneEntitiesManager.setupDraftRectangle(
.setupDraftRectangle(
sketchDetails.sketchPathToNode,
sketchDetails.zAxis,
sketchDetails.yAxis,
sketchDetails.origin,
event.data
)
.then(() => {
return codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
})
},
'set up draft center rectangle': ({
context: { sketchDetails },
event,
}) => {
if (event.type !== 'Add center rectangle origin') return
if (!sketchDetails || !event.data) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises
sceneEntitiesManager.setupDraftCenterRectangle(
sketchDetails.sketchPathToNode, sketchDetails.sketchPathToNode,
sketchDetails.zAxis, sketchDetails.zAxis,
sketchDetails.yAxis, sketchDetails.yAxis,
@ -915,36 +846,26 @@ export const modelingMachine = setup({
'set up draft circle': ({ context: { sketchDetails }, event }) => { 'set up draft circle': ({ context: { sketchDetails }, event }) => {
if (event.type !== 'Add circle origin') return if (event.type !== 'Add circle origin') return
if (!sketchDetails || !event.data) return if (!sketchDetails || !event.data) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
sceneEntitiesManager sceneEntitiesManager.setupDraftCircle(
.setupDraftCircle( sketchDetails.sketchPathToNode,
sketchDetails.sketchPathToNode, sketchDetails.zAxis,
sketchDetails.zAxis, sketchDetails.yAxis,
sketchDetails.yAxis, sketchDetails.origin,
sketchDetails.origin, event.data
event.data )
)
.then(() => {
return codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
})
}, },
'set up draft line without teardown': ({ context: { sketchDetails } }) => { 'set up draft line without teardown': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return if (!sketchDetails) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
sceneEntitiesManager sceneEntitiesManager.setUpDraftSegment(
.setupDraftSegment( sketchDetails.sketchPathToNode,
sketchDetails.sketchPathToNode, sketchDetails.zAxis,
sketchDetails.zAxis, sketchDetails.yAxis,
sketchDetails.yAxis, sketchDetails.origin,
sketchDetails.origin, 'line',
'line', false
false )
)
.then(() => {
return codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
})
}, },
'show default planes': () => { 'show default planes': () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
@ -961,17 +882,12 @@ export const modelingMachine = setup({
'add axis n grid': ({ context: { sketchDetails } }) => { 'add axis n grid': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return if (!sketchDetails) return
if (localStorage.getItem('disableAxis')) return if (localStorage.getItem('disableAxis')) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises
sceneEntitiesManager.createSketchAxis( sceneEntitiesManager.createSketchAxis(
sketchDetails.sketchPathToNode || [], sketchDetails.sketchPathToNode || [],
sketchDetails.zAxis, sketchDetails.zAxis,
sketchDetails.yAxis, sketchDetails.yAxis,
sketchDetails.origin sketchDetails.origin
) )
// eslint-disable-next-line @typescript-eslint/no-floating-promises
codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
}, },
'reset client scene mouse handlers': () => { 'reset client scene mouse handlers': () => {
// when not in sketch mode we don't need any mouse listeners // when not in sketch mode we don't need any mouse listeners
@ -1000,13 +916,10 @@ export const modelingMachine = setup({
'Delete segment': ({ context: { sketchDetails }, event }) => { 'Delete segment': ({ context: { sketchDetails }, event }) => {
if (event.type !== 'Delete segment') return if (event.type !== 'Delete segment') return
if (!sketchDetails || !event.data) return if (!sketchDetails || !event.data) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
deleteSegment({ deleteSegment({
pathToNode: event.data, pathToNode: event.data,
sketchDetails, sketchDetails,
}).then(() => {
return codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
}) })
}, },
'Reset Segment Overlays': () => sceneEntitiesManager.resetOverlays(), 'Reset Segment Overlays': () => sceneEntitiesManager.resetOverlays(),
@ -1071,9 +984,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
return { return {
selectionType: 'completeSelection', selectionType: 'completeSelection',
selection: updateSelections( selection: updateSelections(
@ -1108,7 +1018,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
return { return {
selectionType: 'completeSelection', selectionType: 'completeSelection',
selection: updateSelections( selection: updateSelections(
@ -1143,7 +1052,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
return { return {
selectionType: 'completeSelection', selectionType: 'completeSelection',
selection: updateSelections( selection: updateSelections(
@ -1176,7 +1084,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
const updatedSelectionRanges = updateSelections( const updatedSelectionRanges = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -1210,7 +1117,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
const updatedSelectionRanges = updateSelections( const updatedSelectionRanges = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -1244,7 +1150,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
const updatedSelectionRanges = updateSelections( const updatedSelectionRanges = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -1278,7 +1183,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
const updatedSelectionRanges = updateSelections( const updatedSelectionRanges = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -1316,8 +1220,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
const updatedSelectionRanges = updateSelections( const updatedSelectionRanges = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -1350,7 +1252,6 @@ export const modelingMachine = setup({
) )
if (trap(updatedAst, { suppress: true })) return if (trap(updatedAst, { suppress: true })) return
if (!updatedAst) return if (!updatedAst) return
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
const updatedSelectionRanges = updateSelections( const updatedSelectionRanges = updateSelections(
pathToNodeMap, pathToNodeMap,
selectionRanges, selectionRanges,
@ -1403,7 +1304,7 @@ export const modelingMachine = setup({
} }
), ),
'animate-to-face': fromPromise( 'animate-to-face': fromPromise(
async (_: { input?: ExtrudeFacePlane | DefaultPlane | OffsetPlane }) => { async (_: { input?: ExtrudeFacePlane | DefaultPlane }) => {
return {} as return {} as
| undefined | undefined
| { | {
@ -1655,7 +1556,7 @@ export const modelingMachine = setup({
}, },
}, },
entry: ['setup client side sketch segments'], entry: 'setup client side sketch segments',
}, },
'Await horizontal distance info': { 'Await horizontal distance info': {
@ -1875,40 +1776,6 @@ export const modelingMachine = setup({
}, },
}, },
'Center Rectangle tool': {
entry: ['listen for center rectangle origin'],
states: {
'Awaiting corner': {
on: {
'Finish center rectangle': 'Finished Center Rectangle',
},
},
'Awaiting origin': {
on: {
'Add center rectangle origin': {
target: 'Awaiting corner',
// TODO
actions: 'set up draft center rectangle',
},
},
},
'Finished Center Rectangle': {
always: '#Modeling.Sketch.SketchIdle',
},
},
initial: 'Awaiting origin',
on: {
'change tool': {
target: 'Change Tool',
},
},
},
'clean slate': { 'clean slate': {
always: 'SketchIdle', always: 'SketchIdle',
}, },
@ -1934,7 +1801,7 @@ export const modelingMachine = setup({
onError: 'SketchIdle', onError: 'SketchIdle',
onDone: { onDone: {
target: 'SketchIdle', target: 'SketchIdle',
actions: 'Set selection', actions: ['Set selection'],
}, },
}, },
}, },
@ -2102,10 +1969,6 @@ export const modelingMachine = setup({
target: 'Circle tool', target: 'Circle tool',
guard: 'next is circle', guard: 'next is circle',
}, },
{
target: 'Center Rectangle tool',
guard: 'next is center rectangle',
},
], ],
entry: 'assign tool in context', entry: 'assign tool in context',

View File

@ -737,7 +737,7 @@ dependencies = [
[[package]] [[package]]
name = "derive-docs" name = "derive-docs"
version = "0.1.30" version = "0.1.29"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"anyhow", "anyhow",
@ -1589,8 +1589,6 @@ dependencies = [
"console", "console",
"lazy_static", "lazy_static",
"linked-hash-map", "linked-hash-map",
"pest",
"pest_derive",
"regex", "regex",
"serde", "serde",
"similar", "similar",
@ -1691,7 +1689,6 @@ dependencies = [
"databake", "databake",
"derive-docs", "derive-docs",
"expectorate", "expectorate",
"fnv",
"form_urlencoded", "form_urlencoded",
"futures", "futures",
"git_rev", "git_rev",
@ -1737,6 +1734,18 @@ dependencies = [
"zip", "zip",
] ]
[[package]]
name = "kcl-macros"
version = "0.1.0"
dependencies = [
"databake",
"kcl-lib",
"pretty_assertions",
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]] [[package]]
name = "kcl-test-server" name = "kcl-test-server"
version = "0.1.16" version = "0.1.16"

View File

@ -65,9 +65,11 @@ similar.opt-level = 3
debug = "line-tables-only" debug = "line-tables-only"
[workspace] [workspace]
resolver = "2"
members = [ members = [
"derive-docs", "derive-docs",
"kcl", "kcl",
"kcl-macros",
"kcl-test-server", "kcl-test-server",
"kcl-to-core", "kcl-to-core",
] ]

View File

@ -1,7 +1,7 @@
[package] [package]
name = "derive-docs" name = "derive-docs"
description = "A tool for generating documentation from Rust derive macros" description = "A tool for generating documentation from Rust derive macros"
version = "0.1.30" version = "0.1.29"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -173,11 +173,11 @@ fn do_stdlib_inner(
quote! { quote! {
let code_blocks = vec![#(#cb),*]; let code_blocks = vec![#(#cb),*];
code_blocks.iter().map(|cb| { code_blocks.iter().map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}).collect::<Vec<String>>() }).collect::<Vec<String>>()
} }
} else { } else {
@ -748,7 +748,8 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
quote! { quote! {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn #test_name_mock() { async fn #test_name_mock() {
let program = crate::Program::parse(#code_block).unwrap(); let program = crate::parser::top_level_parse(#code_block).unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())), engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())),
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
@ -757,7 +758,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()).await.unwrap(); ctx.run(&program, None, id_generator, None).await.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]

View File

@ -2,7 +2,8 @@
mod test_examples_someFn { mod test_examples_someFn {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_someFn0() { async fn test_mock_example_someFn0() {
let program = crate::Program::parse("someFn()").unwrap(); let program = crate::parser::top_level_parse("someFn()").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -14,9 +15,7 @@ mod test_examples_someFn {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +111,10 @@ impl crate::docs::StdLibFn for SomeFn {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,7 +2,8 @@
mod test_examples_someFn { mod test_examples_someFn {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_someFn0() { async fn test_mock_example_someFn0() {
let program = crate::Program::parse("someFn()").unwrap(); let program = crate::parser::top_level_parse("someFn()").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -14,9 +15,7 @@ mod test_examples_someFn {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +111,10 @@ impl crate::docs::StdLibFn for SomeFn {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -3,7 +3,9 @@ mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show0() { async fn test_mock_example_show0() {
let program = let program =
crate::Program::parse("This is another code block.\nyes sirrr.\nshow").unwrap(); crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nshow")
.unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -15,9 +17,7 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -36,7 +36,9 @@ mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show1() { async fn test_mock_example_show1() {
let program = crate::Program::parse("This is code.\nIt does other shit.\nshow").unwrap(); let program =
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -48,9 +50,7 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -149,10 +149,10 @@ impl crate::docs::StdLibFn for Show {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,7 +2,9 @@
mod test_examples_show { mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show0() { async fn test_mock_example_show0() {
let program = crate::Program::parse("This is code.\nIt does other shit.\nshow").unwrap(); let program =
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -14,9 +16,7 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Show {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -3,7 +3,9 @@ mod test_examples_my_func {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_my_func0() { async fn test_mock_example_my_func0() {
let program = let program =
crate::Program::parse("This is another code block.\nyes sirrr.\nmyFunc").unwrap(); crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmyFunc")
.unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -15,9 +17,7 @@ mod test_examples_my_func {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -36,7 +36,9 @@ mod test_examples_my_func {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_my_func1() { async fn test_mock_example_my_func1() {
let program = crate::Program::parse("This is code.\nIt does other shit.\nmyFunc").unwrap(); let program =
crate::parser::top_level_parse("This is code.\nIt does other shit.\nmyFunc").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -48,9 +50,7 @@ mod test_examples_my_func {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -149,10 +149,10 @@ impl crate::docs::StdLibFn for MyFunc {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -3,7 +3,9 @@ mod test_examples_line_to {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_line_to0() { async fn test_mock_example_line_to0() {
let program = let program =
crate::Program::parse("This is another code block.\nyes sirrr.\nlineTo").unwrap(); crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nlineTo")
.unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -15,9 +17,7 @@ mod test_examples_line_to {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -36,7 +36,9 @@ mod test_examples_line_to {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_line_to1() { async fn test_mock_example_line_to1() {
let program = crate::Program::parse("This is code.\nIt does other shit.\nlineTo").unwrap(); let program =
crate::parser::top_level_parse("This is code.\nIt does other shit.\nlineTo").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -48,9 +50,7 @@ mod test_examples_line_to {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -157,10 +157,10 @@ impl crate::docs::StdLibFn for LineTo {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -3,7 +3,8 @@ mod test_examples_min {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_min0() { async fn test_mock_example_min0() {
let program = let program =
crate::Program::parse("This is another code block.\nyes sirrr.\nmin").unwrap(); crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmin").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -15,9 +16,7 @@ mod test_examples_min {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -36,7 +35,9 @@ mod test_examples_min {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_min1() { async fn test_mock_example_min1() {
let program = crate::Program::parse("This is code.\nIt does other shit.\nmin").unwrap(); let program =
crate::parser::top_level_parse("This is code.\nIt does other shit.\nmin").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -48,9 +49,7 @@ mod test_examples_min {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -149,10 +148,10 @@ impl crate::docs::StdLibFn for Min {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,7 +2,9 @@
mod test_examples_show { mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show0() { async fn test_mock_example_show0() {
let program = crate::Program::parse("This is code.\nIt does other shit.\nshow").unwrap(); let program =
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -14,9 +16,7 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Show {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,7 +2,9 @@
mod test_examples_import { mod test_examples_import {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_import0() { async fn test_mock_example_import0() {
let program = crate::Program::parse("This is code.\nIt does other shit.\nimport").unwrap(); let program =
crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -14,9 +16,7 @@ mod test_examples_import {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Import {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,7 +2,9 @@
mod test_examples_import { mod test_examples_import {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_import0() { async fn test_mock_example_import0() {
let program = crate::Program::parse("This is code.\nIt does other shit.\nimport").unwrap(); let program =
crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -14,9 +16,7 @@ mod test_examples_import {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, &mut crate::ExecState::default()) ctx.run(&program, None, id_generator, None).await.unwrap();
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Import {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::Program::parse(cb).unwrap(); let program = crate::parser::top_level_parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.ast.recast(&options, 0) program.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

Some files were not shown because too many files have changed in this diff Show More