Compare commits
23 Commits
v0.24.5
...
jtran/type
Author | SHA1 | Date | |
---|---|---|---|
602f27737f | |||
38adaf8c68 | |||
971471f45d | |||
92b3f6192d | |||
2ff1d1167f | |||
b2fe72f677 | |||
8e5d13315d | |||
43a34b191e | |||
19a93e8deb | |||
b8c623e1ec | |||
4006c28479 | |||
8c932fdb8d | |||
a74c715c01 | |||
1ac39d95f2 | |||
41b1ec94fa | |||
525c803888 | |||
2ee1c78aad | |||
dc21034b86 | |||
1684786659 | |||
12505b4398 | |||
115f2fdea2 | |||
0df28abc4b | |||
1e07ea4986 |
@ -4179,12 +4179,15 @@ test.describe('Sketch tests', () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).toBeEnabled()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await expect(async () => {
|
||||
await page.mouse.click(700, 200)
|
||||
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).toBeEnabled({ timeout: 1000 })
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
||||
|
||||
await page.waitForTimeout(600) // wait for animation
|
||||
|
||||
@ -7290,15 +7293,15 @@ test.describe('Test network and connection issues', () => {
|
||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)
|
||||
|> line([-9.16, 8.81], %)`)
|
||||
|> line([-8.84, 8.75], %)`)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)
|
||||
|> line([-9.16, 8.81], %)
|
||||
|> line([-5.28, 0], %)`)
|
||||
|> line([-8.84, 8.75], %)
|
||||
|> line([-5.6, 0], %)`)
|
||||
|
||||
// Unequip line tool
|
||||
await page.keyboard.press('Escape')
|
||||
@ -8094,3 +8097,34 @@ test('Sketch on face', async ({ page }) => {
|
||||
const sketch002 = extrude(${[5, 5]} + 7, sketch002)`
|
||||
await expect(page.locator('.cm-content')).toHaveText(result2.regExp)
|
||||
})
|
||||
|
||||
test('Typing KCL errors induces a badge on the error logs pane button', async ({
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Load the app with the working starter code
|
||||
await page.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, bracket)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
// Ensure no badge is present
|
||||
const errorLogsButton = page.getByRole('button', { name: 'KCL Errors pane' })
|
||||
await expect(errorLogsButton).not.toContainText('notification')
|
||||
|
||||
// Delete a character to break the KCL
|
||||
await u.openKclCodePanel()
|
||||
await page.getByText('extrude(').click()
|
||||
await page.keyboard.press('Backspace')
|
||||
|
||||
// Ensure that a badge appears on the button
|
||||
await expect(errorLogsButton).toContainText('notification')
|
||||
})
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 36 KiB |
Binary file not shown.
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 39 KiB |
@ -266,7 +266,7 @@ export async function getUtils(page: Page) {
|
||||
getSegmentBodyCoords: async (locator: string, px = 30) => {
|
||||
const overlay = page.locator(locator)
|
||||
const bbox = await overlay
|
||||
.boundingBox()
|
||||
.boundingBox({ timeout: 5000 })
|
||||
.then((box) => ({ ...box, x: box?.x || 0, y: box?.y || 0 }))
|
||||
const angle = Number(await overlay.getAttribute('data-overlay-angle'))
|
||||
const angleXOffset = Math.cos(((angle - 180) * Math.PI) / 180) * px
|
||||
|
340
openapi/machine-api.json
Normal file
340
openapi/machine-api.json
Normal file
@ -0,0 +1,340 @@
|
||||
{
|
||||
"components": {
|
||||
"responses": {
|
||||
"Error": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Error"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"Error": {
|
||||
"description": "Error information from a response.",
|
||||
"properties": {
|
||||
"error_code": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"request_id": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message",
|
||||
"request_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Machine": {
|
||||
"description": "Details for a 3d printer connected over USB.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Details for a 3d printer connected over USB.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"manufacturer": {
|
||||
"type": "string"
|
||||
},
|
||||
"model": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"UsbPrinter"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"manufacturer",
|
||||
"model",
|
||||
"port",
|
||||
"type"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"description": "Details for a 3d printer connected over USB.",
|
||||
"properties": {
|
||||
"hostname": {
|
||||
"description": "The hostname of the printer.",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"ip": {
|
||||
"description": "The IP address of the printer.",
|
||||
"format": "ip",
|
||||
"type": "string"
|
||||
},
|
||||
"manufacturer": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NetworkPrinterManufacturer"
|
||||
}
|
||||
],
|
||||
"description": "The manufacturer of the printer."
|
||||
},
|
||||
"model": {
|
||||
"description": "The model of the printer.",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"description": "The port of the printer.",
|
||||
"format": "uint16",
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "integer"
|
||||
},
|
||||
"serial": {
|
||||
"description": "The serial number of the printer.",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"NetworkPrinter"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"ip",
|
||||
"manufacturer",
|
||||
"type"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"NetworkPrinterManufacturer": {
|
||||
"description": "Network printer manufacturer.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Bambu.",
|
||||
"enum": [
|
||||
"Bambu"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Formlabs.",
|
||||
"enum": [
|
||||
"Formlabs"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Pong": {
|
||||
"description": "The response from the `/ping` endpoint.",
|
||||
"properties": {
|
||||
"message": {
|
||||
"description": "The pong response.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"PrintJobResponse": {
|
||||
"description": "The response from the `/print` endpoint.",
|
||||
"properties": {
|
||||
"job_id": {
|
||||
"description": "The job id used for this print.",
|
||||
"type": "string"
|
||||
},
|
||||
"parameters": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PrintParameters"
|
||||
}
|
||||
],
|
||||
"description": "The parameters used for this print."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"job_id",
|
||||
"parameters"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"PrintParameters": {
|
||||
"description": "Parameters for printing.",
|
||||
"properties": {
|
||||
"machine_id": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"machine_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"contact": {
|
||||
"email": "machine-api@zoo.dev",
|
||||
"url": "https://zoo.dev"
|
||||
},
|
||||
"description": "",
|
||||
"title": "machine-api",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"openapi": "3.0.3",
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"operationId": "api_get_schema",
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "successful operation"
|
||||
},
|
||||
"4XX": {
|
||||
"$ref": "#/components/responses/Error"
|
||||
},
|
||||
"5XX": {
|
||||
"$ref": "#/components/responses/Error"
|
||||
}
|
||||
},
|
||||
"summary": "Return the OpenAPI schema in JSON format.",
|
||||
"tags": [
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/machines": {
|
||||
"get": {
|
||||
"operationId": "get_machines",
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/Machine"
|
||||
},
|
||||
"title": "Map_of_Machine",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "successful operation"
|
||||
},
|
||||
"4XX": {
|
||||
"$ref": "#/components/responses/Error"
|
||||
},
|
||||
"5XX": {
|
||||
"$ref": "#/components/responses/Error"
|
||||
}
|
||||
},
|
||||
"summary": "List available machines and their statuses",
|
||||
"tags": [
|
||||
"print"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ping": {
|
||||
"get": {
|
||||
"operationId": "ping",
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Pong"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "successful operation"
|
||||
},
|
||||
"4XX": {
|
||||
"$ref": "#/components/responses/Error"
|
||||
},
|
||||
"5XX": {
|
||||
"$ref": "#/components/responses/Error"
|
||||
}
|
||||
},
|
||||
"summary": "Return pong.",
|
||||
"tags": [
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/print": {
|
||||
"post": {
|
||||
"operationId": "print_file",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"multipart/form-data": {
|
||||
"schema": {
|
||||
"format": "binary",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PrintJobResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "successful operation"
|
||||
},
|
||||
"4XX": {
|
||||
"$ref": "#/components/responses/Error"
|
||||
},
|
||||
"5XX": {
|
||||
"$ref": "#/components/responses/Error"
|
||||
}
|
||||
},
|
||||
"summary": "Print a given file. File must be a sliceable 3D model.",
|
||||
"tags": [
|
||||
"print"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"description": "Meta information about the API.",
|
||||
"externalDocs": {
|
||||
"url": "https://docs.zoo.dev/api/meta"
|
||||
},
|
||||
"name": "meta"
|
||||
},
|
||||
{
|
||||
"description": "Utilities for printing and discovering printers.",
|
||||
"externalDocs": {
|
||||
"url": "https://docs.zoo.dev/api/print"
|
||||
},
|
||||
"name": "print"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.24.5",
|
||||
"version": "0.24.6",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.17.0",
|
||||
|
73
src-tauri/Cargo.lock
generated
73
src-tauri/Cargo.lock
generated
@ -188,7 +188,7 @@ dependencies = [
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-updater",
|
||||
"tokio",
|
||||
"toml 0.8.16",
|
||||
"toml 0.8.19",
|
||||
"url",
|
||||
]
|
||||
|
||||
@ -648,6 +648,12 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder-lite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
@ -721,7 +727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"toml 0.8.16",
|
||||
"toml 0.8.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -792,9 +798,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.11"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
|
||||
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -802,9 +808,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.11"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
|
||||
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -816,9 +822,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.11"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
|
||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@ -1383,7 +1389,7 @@ dependencies = [
|
||||
"cc",
|
||||
"memchr",
|
||||
"rustc_version",
|
||||
"toml 0.8.16",
|
||||
"toml 0.8.19",
|
||||
"vswhom",
|
||||
"winreg 0.52.0",
|
||||
]
|
||||
@ -2399,6 +2405,18 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"num-traits",
|
||||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@ -2571,7 +2589,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx",
|
||||
@ -2589,6 +2607,7 @@ dependencies = [
|
||||
"futures",
|
||||
"git_rev",
|
||||
"gltf-json",
|
||||
"image",
|
||||
"js-sys",
|
||||
"kittycad",
|
||||
"lazy_static",
|
||||
@ -2603,7 +2622,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"toml 0.8.16",
|
||||
"toml 0.8.19",
|
||||
"tower-lsp",
|
||||
"ts-rs",
|
||||
"url",
|
||||
@ -3520,9 +3539,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phonenumber"
|
||||
version = "0.3.5+8.13.36"
|
||||
version = "0.3.6+8.13.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f174c8db59b620032bd52b655fc97000458850fec0db35fcd4e802b668517ec0"
|
||||
checksum = "11756237b57b8cc5e97dc8b1e70ea436324d30e7075de63b14fd15073a8f692a"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"either",
|
||||
@ -5069,7 +5088,7 @@ dependencies = [
|
||||
"cfg-expr",
|
||||
"heck 0.5.0",
|
||||
"pkg-config",
|
||||
"toml 0.8.16",
|
||||
"toml 0.8.19",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
@ -5222,7 +5241,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"tauri-winres",
|
||||
"toml 0.8.16",
|
||||
"toml 0.8.19",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -5280,7 +5299,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"toml 0.8.16",
|
||||
"toml 0.8.19",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -5564,7 +5583,7 @@ dependencies = [
|
||||
"serde_with",
|
||||
"swift-rs",
|
||||
"thiserror",
|
||||
"toml 0.8.16",
|
||||
"toml 0.8.19",
|
||||
"url",
|
||||
"urlpattern",
|
||||
"walkdir",
|
||||
@ -5811,21 +5830,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.16"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.22.17",
|
||||
"toml_edit 0.22.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.7"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -5867,15 +5886,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.17"
|
||||
version = "0.22.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
dependencies = [
|
||||
"indexmap 2.2.6",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.6.6",
|
||||
"winnow 0.6.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6946,9 +6965,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.6"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -80,5 +80,5 @@
|
||||
}
|
||||
},
|
||||
"productName": "Zoo Modeling App",
|
||||
"version": "0.24.5"
|
||||
"version": "0.24.6"
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
||||
import { ReactCameraProperties } from './CameraControls'
|
||||
import { throttle } from 'lib/utils'
|
||||
import { isArray, throttle } from 'lib/utils'
|
||||
import {
|
||||
sceneInfra,
|
||||
kclManager,
|
||||
@ -20,13 +20,17 @@ import {
|
||||
getParentGroup,
|
||||
} from './sceneEntities'
|
||||
import { SegmentOverlay, SketchDetails } from 'machines/modelingMachine'
|
||||
import { findUsesOfTagInPipe, getNodeFromPath } from 'lang/queryAst'
|
||||
import {
|
||||
expectNodeOnPath,
|
||||
findUsesOfTagInPipe,
|
||||
getLastNodeFromPath,
|
||||
getNodeFromPath,
|
||||
} from 'lang/queryAst'
|
||||
import {
|
||||
CallExpression,
|
||||
PathToNode,
|
||||
Program,
|
||||
SourceRange,
|
||||
Value,
|
||||
parse,
|
||||
recast,
|
||||
} from 'lang/wasm'
|
||||
@ -186,13 +190,12 @@ const Overlay = ({
|
||||
let xAlignment = overlay.angle < 0 ? '0%' : '-100%'
|
||||
let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%'
|
||||
|
||||
const _node1 = getNodeFromPath<CallExpression>(
|
||||
const callExpression = expectNodeOnPath<CallExpression>(
|
||||
kclManager.ast,
|
||||
overlay.pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(_node1)) return
|
||||
const callExpression = _node1.node
|
||||
if (err(callExpression)) return
|
||||
|
||||
const constraints = getConstraintInfo(
|
||||
callExpression,
|
||||
@ -549,13 +552,13 @@ const ConstraintSymbol = ({
|
||||
varNameMap[_type as LineInputsType]?.implicitConstraintDesc
|
||||
|
||||
const _node = useMemo(
|
||||
() => getNodeFromPath<Value>(kclManager.ast, pathToNode),
|
||||
() => getLastNodeFromPath(kclManager.ast, pathToNode),
|
||||
[kclManager.ast, pathToNode]
|
||||
)
|
||||
if (err(_node)) return
|
||||
const node = _node.node
|
||||
|
||||
const range: SourceRange = node ? [node.start, node.end] : [0, 0]
|
||||
const range: SourceRange = !isArray(node) ? [node.start, node.end] : [0, 0]
|
||||
|
||||
if (_type === 'intersectionTag') return null
|
||||
|
||||
|
@ -61,7 +61,12 @@ import {
|
||||
codeManager,
|
||||
editorManager,
|
||||
} from 'lib/singletons'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
import {
|
||||
isNodeType,
|
||||
expectNodeOnPath,
|
||||
getNodeFromPath,
|
||||
getNodePathFromSourceRange,
|
||||
} from 'lang/queryAst'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import {
|
||||
createArcGeometry,
|
||||
@ -77,7 +82,13 @@ import {
|
||||
changeSketchArguments,
|
||||
updateStartProfileAtArgs,
|
||||
} from 'lang/std/sketch'
|
||||
import { isOverlap, normaliseAngle, roundOff, throttle } from 'lib/utils'
|
||||
import {
|
||||
isArray,
|
||||
isOverlap,
|
||||
normaliseAngle,
|
||||
roundOff,
|
||||
throttle,
|
||||
} from 'lib/utils'
|
||||
import {
|
||||
addStartProfileAt,
|
||||
createArrayExpression,
|
||||
@ -463,13 +474,18 @@ export class SceneEntities {
|
||||
)
|
||||
|
||||
let seg: Group
|
||||
const _node1 = getNodeFromPath<CallExpression>(
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
maybeModdedAst,
|
||||
segPathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(_node1)) return
|
||||
const callExpName = _node1.node?.callee?.name
|
||||
if (err(callExp)) return
|
||||
const callExpName = isNodeType<CallExpression>(
|
||||
callExp.node,
|
||||
'CallExpression'
|
||||
)
|
||||
? callExp.node.callee.name
|
||||
: ''
|
||||
|
||||
if (segment.type === 'TangentialArcTo') {
|
||||
seg = tangentialArcToSegment({
|
||||
@ -590,8 +606,12 @@ export class SceneEntities {
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (trap(_node1)) return Promise.reject(_node1)
|
||||
const variableDeclarationName =
|
||||
_node1.node?.declarations?.[0]?.id?.name || ''
|
||||
const variableDeclarationName = isNodeType<VariableDeclaration>(
|
||||
_node1.node,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
? _node1.node.declarations[0]?.id?.name || ''
|
||||
: ''
|
||||
|
||||
const sg = kclManager.programMemory.get(
|
||||
variableDeclarationName
|
||||
@ -728,15 +748,14 @@ export class SceneEntities {
|
||||
) => {
|
||||
let _ast = structuredClone(kclManager.ast)
|
||||
|
||||
const _node1 = getNodeFromPath<VariableDeclaration>(
|
||||
const varDec = expectNodeOnPath<VariableDeclaration>(
|
||||
_ast,
|
||||
sketchPathToNode || [],
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (trap(_node1)) return Promise.reject(_node1)
|
||||
const variableDeclarationName =
|
||||
_node1.node?.declarations?.[0]?.id?.name || ''
|
||||
const startSketchOn = _node1.node?.declarations
|
||||
if (trap(varDec)) return Promise.reject(varDec)
|
||||
const variableDeclarationName = varDec.declarations?.[0]?.id?.name || ''
|
||||
const startSketchOn = varDec.declarations
|
||||
const startSketchOnInit = startSketchOn?.[0]?.init
|
||||
|
||||
const tags: [string, string, string] = [
|
||||
@ -775,12 +794,17 @@ export class SceneEntities {
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (trap(_node)) return Promise.reject(_node)
|
||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||
const sketchInit = isNodeType<VariableDeclaration>(
|
||||
_node.node,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
? _node.node.declarations[0]?.init
|
||||
: null
|
||||
|
||||
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') {
|
||||
updateRectangleSketch(sketchInit, x, y, tags[0])
|
||||
}
|
||||
|
||||
@ -823,9 +847,14 @@ export class SceneEntities {
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (trap(_node)) return Promise.reject(_node)
|
||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||
const sketchInit = isNodeType<VariableDeclaration>(
|
||||
_node.node,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
? _node.node.declarations[0]?.init
|
||||
: null
|
||||
|
||||
if (sketchInit.type === 'PipeExpression') {
|
||||
if (sketchInit?.type === 'PipeExpression') {
|
||||
updateRectangleSketch(sketchInit, x, y, tags[0])
|
||||
|
||||
let _recastAst = parse(recast(_ast))
|
||||
@ -1061,7 +1090,7 @@ export class SceneEntities {
|
||||
if (trap(_node)) return
|
||||
const node = _node.node
|
||||
|
||||
if (node.type !== 'CallExpression') return
|
||||
if (isArray(node) || node.type !== 'CallExpression') return
|
||||
|
||||
let modded:
|
||||
| {
|
||||
@ -1666,7 +1695,9 @@ export class SceneEntities {
|
||||
)
|
||||
if (trap(_node, { suppress: true })) return
|
||||
const node = _node.node
|
||||
editorManager.setHighlightRange([node.start, node.end])
|
||||
editorManager.setHighlightRange(
|
||||
!isArray(node) ? [node.start, node.end] : [0, 0]
|
||||
)
|
||||
const yellow = 0xffff00
|
||||
colorSegment(selected, yellow)
|
||||
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||
@ -1796,7 +1827,14 @@ function prepareTruncatedMemoryAndAst(
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(_node)) return _node
|
||||
const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || ''
|
||||
if (isArray(_node.node))
|
||||
return new Error('Expected node to be an object, but found Array')
|
||||
const variableDeclarationName = isNodeType<VariableDeclaration>(
|
||||
_node.node,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
? _node.node.declarations[0]?.id?.name || ''
|
||||
: ''
|
||||
const lastSeg = (
|
||||
programMemory.get(variableDeclarationName) as SketchGroup
|
||||
).value.slice(-1)[0]
|
||||
@ -1919,7 +1957,12 @@ export function sketchGroupFromPathToNode({
|
||||
)
|
||||
if (err(_varDec)) return _varDec
|
||||
const varDec = _varDec.node
|
||||
const result = programMemory.get(varDec?.id?.name || '')
|
||||
if (isArray(varDec))
|
||||
return new Error('Expected node to be an object, but found Array')
|
||||
const varName = isNodeType<VariableDeclarator>(varDec, 'VariableDeclarator')
|
||||
? varDec.id.name
|
||||
: ''
|
||||
const result = programMemory.get(varName)
|
||||
if (result?.type === 'ExtrudeGroup') {
|
||||
return result.sketchGroup
|
||||
}
|
||||
|
@ -142,7 +142,9 @@ export const ModelingMachineProvider = ({
|
||||
kclManager.executeCode().then(() => {
|
||||
if (engineCommandManager.engineConnection?.idleMode) return
|
||||
|
||||
store.videoElement?.play()
|
||||
store.videoElement?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e)
|
||||
})
|
||||
})
|
||||
})()
|
||||
},
|
||||
|
@ -14,6 +14,7 @@ import { MemoryPane, MemoryPaneMenu } from './MemoryPane'
|
||||
import { KclErrorsPane, LogsPane } from './LoggingPanes'
|
||||
import { DebugPane } from './DebugPane'
|
||||
import { FileTreeInner, FileTreeMenu } from 'components/FileTree'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
|
||||
export type SidebarType =
|
||||
| 'code'
|
||||
@ -25,6 +26,14 @@ export type SidebarType =
|
||||
| 'lspMessages'
|
||||
| 'variables'
|
||||
|
||||
/**
|
||||
* This interface can be extended as more context is needed for the panes
|
||||
* to determine if they should show their badges or not.
|
||||
*/
|
||||
interface PaneCallbackProps {
|
||||
kclContext: ReturnType<typeof useKclContext>
|
||||
}
|
||||
|
||||
export type SidebarPane = {
|
||||
id: SidebarType
|
||||
title: string
|
||||
@ -33,6 +42,7 @@ export type SidebarPane = {
|
||||
Content: ReactNode | React.FC
|
||||
Menu?: ReactNode | React.FC
|
||||
hideOnPlatform?: 'desktop' | 'web'
|
||||
showBadge?: (props: PaneCallbackProps) => boolean | number
|
||||
}
|
||||
|
||||
export const sidebarPanes: SidebarPane[] = [
|
||||
@ -74,6 +84,7 @@ export const sidebarPanes: SidebarPane[] = [
|
||||
icon: faExclamationCircle,
|
||||
Content: KclErrorsPane,
|
||||
keybinding: 'Shift + E',
|
||||
showBadge: ({ kclContext }) => kclContext.errors.length,
|
||||
},
|
||||
{
|
||||
id: 'debug',
|
||||
|
@ -12,6 +12,7 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { CustomIconName } from 'components/CustomIcon'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
|
||||
interface ModelingSidebarProps {
|
||||
paneOpacity: '' | 'opacity-20' | 'opacity-40'
|
||||
@ -19,6 +20,7 @@ interface ModelingSidebarProps {
|
||||
|
||||
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
const { commandBarSend } = useCommandsContext()
|
||||
const kclContext = useKclContext()
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const onboardingStatus = settings.context.app.onboardingStatus
|
||||
const { send, context } = useModelingContext()
|
||||
@ -62,6 +64,15 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
[sidebarPanes, showDebugPanel.current]
|
||||
)
|
||||
|
||||
const paneBadgeMap: Record<SidebarType, number | boolean> = useMemo(() => {
|
||||
return filteredPanes.reduce((acc, pane) => {
|
||||
if (pane.showBadge) {
|
||||
acc[pane.id] = pane.showBadge({ kclContext })
|
||||
}
|
||||
return acc
|
||||
}, {} as Record<SidebarType, number | boolean>)
|
||||
}, [kclContext.errors])
|
||||
|
||||
const togglePane = useCallback(
|
||||
(newPane: SidebarType) => {
|
||||
send({
|
||||
@ -120,6 +131,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
paneIsOpen={context.store?.openPanes.includes(pane.id)}
|
||||
onClick={() => togglePane(pane.id)}
|
||||
aria-pressed={context.store?.openPanes.includes(pane.id)}
|
||||
showBadge={paneBadgeMap[pane.id]}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
@ -186,12 +198,14 @@ interface ModelingPaneButtonProps
|
||||
}
|
||||
onClick: () => void
|
||||
paneIsOpen?: boolean
|
||||
showBadge?: boolean | number
|
||||
}
|
||||
|
||||
function ModelingPaneButton({
|
||||
paneConfig,
|
||||
onClick,
|
||||
paneIsOpen,
|
||||
showBadge,
|
||||
...props
|
||||
}: ModelingPaneButtonProps) {
|
||||
useHotkeys(paneConfig.keybinding, onClick, {
|
||||
@ -223,6 +237,23 @@ function ModelingPaneButton({
|
||||
{paneConfig.title}
|
||||
{paneIsOpen !== undefined ? ` pane` : ''}
|
||||
</span>
|
||||
{!!showBadge && (
|
||||
<p
|
||||
className={
|
||||
'absolute m-0 p-0 -top-1 -right-1 w-3 h-3 flex items-center justify-center text-[10px] font-semibold text-white bg-primary hue-rotate-90 rounded-full border border-chalkboard-10 dark:border-chalkboard-80'
|
||||
}
|
||||
>
|
||||
<span className="sr-only"> has </span>
|
||||
{typeof showBadge === 'number' ? (
|
||||
<span>{showBadge}</span>
|
||||
) : (
|
||||
<span className="sr-only">a</span>
|
||||
)}
|
||||
<span className="sr-only">
|
||||
notification{Number(showBadge) > 1 ? 's' : ''}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
<Tooltip
|
||||
position="right"
|
||||
contentClassName="max-w-none flex items-center gap-4"
|
||||
|
@ -175,11 +175,35 @@ export const SettingsAuthProviderBase = ({
|
||||
id: `${event.type}.success`,
|
||||
})
|
||||
},
|
||||
'Execute AST': () => {
|
||||
kclManager.isFirstRender = true
|
||||
kclManager.executeCode(true).then(() => {
|
||||
kclManager.isFirstRender = false
|
||||
})
|
||||
'Execute AST': (context, event) => {
|
||||
try {
|
||||
const allSettingsIncludesUnitChange =
|
||||
event.type === 'Set all settings' &&
|
||||
event.settings?.modeling?.defaultUnit?.current !==
|
||||
context.modeling.defaultUnit.current
|
||||
const resetSettingsIncludesUnitChange =
|
||||
event.type === 'Reset settings' &&
|
||||
context.modeling.defaultUnit.current !==
|
||||
settings?.modeling?.defaultUnit?.default
|
||||
|
||||
if (
|
||||
event.type === 'set.modeling.defaultUnit' ||
|
||||
allSettingsIncludesUnitChange ||
|
||||
resetSettingsIncludesUnitChange
|
||||
) {
|
||||
kclManager.isFirstRender = true
|
||||
kclManager.executeCode(true).then(() => {
|
||||
kclManager.isFirstRender = false
|
||||
})
|
||||
} else {
|
||||
// For any future logging we'd like to do
|
||||
// console.log(
|
||||
// 'Not re-executing AST because the settings change did not affect the code interpretation'
|
||||
// )
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error executing AST after settings change', e)
|
||||
}
|
||||
},
|
||||
},
|
||||
services: {
|
||||
|
@ -156,7 +156,13 @@ export const Stream = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setIsFirstRender(kclManager.isFirstRender)
|
||||
if (!kclManager.isFirstRender) videoRef.current?.play()
|
||||
if (!kclManager.isFirstRender)
|
||||
setTimeout(() =>
|
||||
// execute in the next event loop
|
||||
videoRef.current?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e, videoRef.current)
|
||||
})
|
||||
)
|
||||
setIsFreezeFrame(!kclManager.isFirstRender)
|
||||
}, [kclManager.isFirstRender])
|
||||
|
||||
@ -170,8 +176,12 @@ export const Stream = () => {
|
||||
if (!mediaStream) return
|
||||
|
||||
// Do not immediately play the stream!
|
||||
videoRef.current.srcObject = mediaStream
|
||||
videoRef.current.pause()
|
||||
try {
|
||||
videoRef.current.srcObject = mediaStream
|
||||
videoRef.current.pause()
|
||||
} catch (e) {
|
||||
console.warn('Attempted to pause stream while play was still loading', e)
|
||||
}
|
||||
|
||||
send({
|
||||
type: 'Set context',
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import { CallExpression, Program, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
getLastNodeFromPath,
|
||||
DynamicNode,
|
||||
isNodeType,
|
||||
expectNodeOnPath,
|
||||
} from '../../lang/queryAst'
|
||||
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
|
||||
import {
|
||||
@ -14,6 +17,7 @@ import {
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { isArray } from 'lib/utils'
|
||||
|
||||
export function equalAngleInfo({
|
||||
selectionRanges,
|
||||
@ -29,26 +33,33 @@ export function equalAngleInfo({
|
||||
getNodePathFromSourceRange(kclManager.ast, range)
|
||||
)
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode)
|
||||
const tmp = getLastNodeFromPath(kclManager.ast, pathToNode)
|
||||
if (err(tmp)) return tmp
|
||||
if (isArray(tmp.node)) {
|
||||
return new Error('Expected value node, but found array')
|
||||
}
|
||||
return tmp.node
|
||||
})
|
||||
const _err1 = _nodes.find(err)
|
||||
if (err(_err1)) return _err1
|
||||
const nodes = _nodes as Value[]
|
||||
const nodes: DynamicNode[] = []
|
||||
for (const node of _nodes) {
|
||||
if (err(node)) return node
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
const _varDecs = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<VariableDeclarator>(
|
||||
const tmp = expectNodeOnPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(tmp)) return tmp
|
||||
return tmp.node
|
||||
return tmp
|
||||
})
|
||||
const _err2 = _varDecs.find(err)
|
||||
if (err(_err2)) return _err2
|
||||
const varDecs = _varDecs as VariableDeclarator[]
|
||||
const varDecs: VariableDeclarator[] = []
|
||||
for (const varDec of _varDecs) {
|
||||
if (err(varDec)) return varDec
|
||||
varDecs.push(varDec)
|
||||
}
|
||||
|
||||
const primaryLine = varDecs[0]
|
||||
const secondaryVarDecs = varDecs.slice(1)
|
||||
@ -57,7 +68,7 @@ export function equalAngleInfo({
|
||||
)
|
||||
const isAllTooltips = nodes.every(
|
||||
(node) =>
|
||||
node?.type === 'CallExpression' &&
|
||||
isNodeType<CallExpression>(node, 'CallExpression') &&
|
||||
toolTips.includes(node.callee.name as any)
|
||||
)
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import { CallExpression, Program, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
getLastNodeFromPath,
|
||||
DynamicNode,
|
||||
isNodeType,
|
||||
expectNodeOnPath,
|
||||
} from '../../lang/queryAst'
|
||||
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
|
||||
import {
|
||||
@ -14,6 +17,7 @@ import {
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { isArray } from 'lib/utils'
|
||||
|
||||
export function setEqualLengthInfo({
|
||||
selectionRanges,
|
||||
@ -29,26 +33,33 @@ export function setEqualLengthInfo({
|
||||
getNodePathFromSourceRange(kclManager.ast, range)
|
||||
)
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode)
|
||||
const tmp = getLastNodeFromPath(kclManager.ast, pathToNode)
|
||||
if (err(tmp)) return tmp
|
||||
if (isArray(tmp.node)) {
|
||||
return new Error('Expected value node, but found array')
|
||||
}
|
||||
return tmp.node
|
||||
})
|
||||
const _err1 = _nodes.find(err)
|
||||
if (err(_err1)) return _err1
|
||||
const nodes = _nodes as Value[]
|
||||
const nodes: DynamicNode[] = []
|
||||
for (const node of _nodes) {
|
||||
if (err(node)) return node
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
const _varDecs = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<VariableDeclarator>(
|
||||
const varDec = expectNodeOnPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(tmp)) return tmp
|
||||
return tmp.node
|
||||
if (err(varDec)) return varDec
|
||||
return varDec
|
||||
})
|
||||
const _err2 = _varDecs.find(err)
|
||||
if (err(_err2)) return _err2
|
||||
const varDecs = _varDecs as VariableDeclarator[]
|
||||
const varDecs: VariableDeclarator[] = []
|
||||
for (const varDec of _varDecs) {
|
||||
if (err(varDec)) return varDec
|
||||
varDecs.push(varDec)
|
||||
}
|
||||
|
||||
const primaryLine = varDecs[0]
|
||||
const secondaryVarDecs = varDecs.slice(1)
|
||||
@ -57,7 +68,7 @@ export function setEqualLengthInfo({
|
||||
)
|
||||
const isAllTooltips = nodes.every(
|
||||
(node) =>
|
||||
node?.type === 'CallExpression' &&
|
||||
isNodeType<CallExpression>(node, 'CallExpression') &&
|
||||
toolTips.includes(node.callee.name as any)
|
||||
)
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, ProgramMemory, Value } from '../../lang/wasm'
|
||||
import { CallExpression, Program, ProgramMemory } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
getLastNodeFromPath,
|
||||
DynamicNode,
|
||||
isNodeType,
|
||||
} from '../../lang/queryAst'
|
||||
import {
|
||||
PathToNodeMap,
|
||||
@ -13,6 +15,7 @@ import {
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { isArray } from 'lib/utils'
|
||||
|
||||
export function horzVertInfo(
|
||||
selectionRanges: Selections,
|
||||
@ -27,17 +30,22 @@ export function horzVertInfo(
|
||||
getNodePathFromSourceRange(kclManager.ast, range)
|
||||
)
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode)
|
||||
const tmp = getLastNodeFromPath(kclManager.ast, pathToNode)
|
||||
if (err(tmp)) return tmp
|
||||
if (isArray(tmp.node)) {
|
||||
return new Error('Expected value node, but found array')
|
||||
}
|
||||
return tmp.node
|
||||
})
|
||||
const _err1 = _nodes.find(err)
|
||||
if (err(_err1)) return _err1
|
||||
const nodes = _nodes as Value[]
|
||||
const nodes: DynamicNode[] = []
|
||||
for (const node of _nodes) {
|
||||
if (err(node)) return node
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
const isAllTooltips = nodes.every(
|
||||
(node) =>
|
||||
node?.type === 'CallExpression' &&
|
||||
isNodeType<CallExpression>(node, 'CallExpression') &&
|
||||
toolTips.includes(node.callee.name as any)
|
||||
)
|
||||
|
||||
|
@ -1,10 +1,18 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
BinaryPart,
|
||||
CallExpression,
|
||||
Program,
|
||||
VariableDeclarator,
|
||||
} from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
isLinesParallelAndConstrained,
|
||||
getLastNodeFromPath,
|
||||
expectNodeOnPath,
|
||||
DynamicNode,
|
||||
isNodeType,
|
||||
} from '../../lang/queryAst'
|
||||
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
|
||||
import {
|
||||
@ -18,6 +26,7 @@ import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { isArray } from 'lib/utils'
|
||||
|
||||
const getModalInfo = createInfoModal(GetInfoModal)
|
||||
|
||||
@ -72,26 +81,33 @@ export function intersectInfo({
|
||||
getNodePathFromSourceRange(kclManager.ast, range)
|
||||
)
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode)
|
||||
const tmp = getLastNodeFromPath(kclManager.ast, pathToNode)
|
||||
if (err(tmp)) return tmp
|
||||
if (isArray(tmp.node)) {
|
||||
return new Error('Expected value node, but found array')
|
||||
}
|
||||
return tmp.node
|
||||
})
|
||||
const _err1 = _nodes.find(err)
|
||||
if (err(_err1)) return _err1
|
||||
const nodes = _nodes as Value[]
|
||||
const nodes: DynamicNode[] = []
|
||||
for (const node of _nodes) {
|
||||
if (err(node)) return node
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
const _varDecs = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<VariableDeclarator>(
|
||||
const varDec = expectNodeOnPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(tmp)) return tmp
|
||||
return tmp.node
|
||||
if (err(varDec)) return varDec
|
||||
return varDec
|
||||
})
|
||||
const _err2 = _varDecs.find(err)
|
||||
if (err(_err2)) return _err2
|
||||
const varDecs = _varDecs as VariableDeclarator[]
|
||||
const varDecs: VariableDeclarator[] = []
|
||||
for (const varDec of _varDecs) {
|
||||
if (err(varDec)) return varDec
|
||||
varDecs.push(varDec)
|
||||
}
|
||||
|
||||
const primaryLine = varDecs[0]
|
||||
const secondaryVarDecs = varDecs.slice(1)
|
||||
@ -100,7 +116,7 @@ export function intersectInfo({
|
||||
)
|
||||
const isAllTooltips = nodes.every(
|
||||
(node) =>
|
||||
node?.type === 'CallExpression' &&
|
||||
isNodeType<CallExpression>(node, 'CallExpression') &&
|
||||
[
|
||||
...toolTips,
|
||||
'startSketchAt', // TODO probably a better place for this to live
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selection, Selections } from 'lib/selections'
|
||||
import { PathToNode, Program, Value } from '../../lang/wasm'
|
||||
import { CallExpression, PathToNode, Program } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
getLastNodeFromPath,
|
||||
DynamicNode,
|
||||
isNodeType,
|
||||
} from '../../lang/queryAst'
|
||||
import {
|
||||
PathToNodeMap,
|
||||
@ -13,6 +15,7 @@ import {
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { isArray } from 'lib/utils'
|
||||
|
||||
export function removeConstrainingValuesInfo({
|
||||
selectionRanges,
|
||||
@ -33,13 +36,18 @@ export function removeConstrainingValuesInfo({
|
||||
getNodePathFromSourceRange(kclManager.ast, range)
|
||||
)
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode)
|
||||
const tmp = getLastNodeFromPath(kclManager.ast, pathToNode)
|
||||
if (err(tmp)) return tmp
|
||||
if (isArray(tmp.node)) {
|
||||
return new Error('Expected value node, but found array')
|
||||
}
|
||||
return tmp.node
|
||||
})
|
||||
const _err1 = _nodes.find(err)
|
||||
if (err(_err1)) return _err1
|
||||
const nodes = _nodes as Value[]
|
||||
const nodes: DynamicNode[] = []
|
||||
for (const node of _nodes) {
|
||||
if (err(node)) return node
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
const updatedSelectionRanges = pathToNodes
|
||||
? {
|
||||
@ -54,7 +62,7 @@ export function removeConstrainingValuesInfo({
|
||||
: selectionRanges
|
||||
const isAllTooltips = nodes.every(
|
||||
(node) =>
|
||||
node?.type === 'CallExpression' &&
|
||||
isNodeType<CallExpression>(node, 'CallExpression') &&
|
||||
toolTips.includes(node.callee.name as any)
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value } from '../../lang/wasm'
|
||||
import { BinaryPart, CallExpression, Program, Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -49,22 +49,22 @@ export function absDistanceInfo({
|
||||
getNodePathFromSourceRange(kclManager.ast, range)
|
||||
)
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<Value>(
|
||||
const tmp = getNodeFromPath<CallExpression>(
|
||||
kclManager.ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(tmp)) return tmp
|
||||
return tmp.node
|
||||
return tmp.stopAtNode
|
||||
})
|
||||
const _err1 = _nodes.find(err)
|
||||
if (err(_err1)) return _err1
|
||||
const nodes = _nodes as Value[]
|
||||
const nodes: (CallExpression | null)[] = []
|
||||
for (const node of _nodes) {
|
||||
if (err(node)) return node
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
const isAllTooltips = nodes.every(
|
||||
(node) =>
|
||||
node?.type === 'CallExpression' &&
|
||||
toolTips.includes(node.callee.name as any)
|
||||
(node) => node && toolTips.includes(node.callee.name as any)
|
||||
)
|
||||
|
||||
const transforms = getTransformInfos(selectionRanges, kclManager.ast, disType)
|
||||
|
@ -1,9 +1,17 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
BinaryPart,
|
||||
CallExpression,
|
||||
Program,
|
||||
VariableDeclarator,
|
||||
} from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
getLastNodeFromPath,
|
||||
DynamicNode,
|
||||
expectNodeOnPath,
|
||||
isNodeType,
|
||||
} from '../../lang/queryAst'
|
||||
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
|
||||
import {
|
||||
@ -17,6 +25,7 @@ import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { isArray } from 'lib/utils'
|
||||
|
||||
const getModalInfo = createInfoModal(GetInfoModal)
|
||||
|
||||
@ -35,26 +44,33 @@ export function angleBetweenInfo({
|
||||
)
|
||||
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode)
|
||||
const tmp = getLastNodeFromPath(kclManager.ast, pathToNode)
|
||||
if (err(tmp)) return tmp
|
||||
if (isArray(tmp.node)) {
|
||||
return new Error('Expected value node, but found array')
|
||||
}
|
||||
return tmp.node
|
||||
})
|
||||
const _err1 = _nodes.find(err)
|
||||
if (err(_err1)) return _err1
|
||||
const nodes = _nodes as Value[]
|
||||
const nodes: DynamicNode[] = []
|
||||
for (const node of _nodes) {
|
||||
if (err(node)) return node
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
const _varDecs = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<VariableDeclarator>(
|
||||
const varDec = expectNodeOnPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(tmp)) return tmp
|
||||
return tmp.node
|
||||
if (err(varDec)) return varDec
|
||||
return varDec
|
||||
})
|
||||
const _err2 = _varDecs.find(err)
|
||||
if (err(_err2)) return _err2
|
||||
const varDecs = _varDecs as VariableDeclarator[]
|
||||
const varDecs: VariableDeclarator[] = []
|
||||
for (const varDec of _varDecs) {
|
||||
if (err(varDec)) return varDec
|
||||
varDecs.push(varDec)
|
||||
}
|
||||
|
||||
const primaryLine = varDecs[0]
|
||||
const secondaryVarDecs = varDecs.slice(1)
|
||||
@ -63,7 +79,7 @@ export function angleBetweenInfo({
|
||||
)
|
||||
const isAllTooltips = nodes.every(
|
||||
(node) =>
|
||||
node?.type === 'CallExpression' &&
|
||||
isNodeType<CallExpression>(node, 'CallExpression') &&
|
||||
toolTips.includes(node.callee.name as any)
|
||||
)
|
||||
|
||||
|
@ -1,8 +1,15 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
BinaryPart,
|
||||
CallExpression,
|
||||
Program,
|
||||
VariableDeclarator,
|
||||
} from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
getLastNodeFromPath,
|
||||
expectNodeOnPath,
|
||||
isNodeType,
|
||||
} from '../../lang/queryAst'
|
||||
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
|
||||
import {
|
||||
@ -17,6 +24,7 @@ import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { cleanErrs, err } from 'lib/trap'
|
||||
import { isArray } from 'lib/utils'
|
||||
|
||||
const getModalInfo = createInfoModal(GetInfoModal)
|
||||
|
||||
@ -36,27 +44,31 @@ export function horzVertDistanceInfo({
|
||||
getNodePathFromSourceRange(kclManager.ast, range)
|
||||
)
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode)
|
||||
const tmp = getLastNodeFromPath(kclManager.ast, pathToNode)
|
||||
if (err(tmp)) return tmp
|
||||
if (isArray(tmp.node)) {
|
||||
return new Error('Expected value node, but found array')
|
||||
}
|
||||
return tmp.node
|
||||
})
|
||||
const [hasErr, , nodesWErrs] = cleanErrs(_nodes)
|
||||
const [hasErr, nodes, nodesWErrs] = cleanErrs(_nodes)
|
||||
|
||||
if (hasErr) return nodesWErrs[0]
|
||||
const nodes = _nodes as Value[]
|
||||
|
||||
const _varDecs = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<VariableDeclarator>(
|
||||
const varDec = expectNodeOnPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(tmp)) return tmp
|
||||
return tmp.node
|
||||
if (err(varDec)) return varDec
|
||||
return varDec
|
||||
})
|
||||
const _err2 = _varDecs.find(err)
|
||||
if (err(_err2)) return _err2
|
||||
const varDecs = _varDecs as VariableDeclarator[]
|
||||
const varDecs: VariableDeclarator[] = []
|
||||
for (const varDec of _varDecs) {
|
||||
if (err(varDec)) return varDec
|
||||
varDecs.push(varDec)
|
||||
}
|
||||
|
||||
const primaryLine = varDecs[0]
|
||||
const secondaryVarDecs = varDecs.slice(1)
|
||||
@ -65,7 +77,7 @@ export function horzVertDistanceInfo({
|
||||
)
|
||||
const isAllTooltips = nodes.every(
|
||||
(node) =>
|
||||
node?.type === 'CallExpression' &&
|
||||
isNodeType<CallExpression>(node, 'CallExpression') &&
|
||||
[
|
||||
...toolTips,
|
||||
'startSketchAt', // TODO probably a better place for this to live
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value } from '../../lang/wasm'
|
||||
import { BinaryPart, CallExpression, Program } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -43,18 +43,24 @@ export function angleLengthInfo({
|
||||
getNodePathFromSourceRange(kclManager.ast, range)
|
||||
)
|
||||
|
||||
const nodes = paths.map((pathToNode) =>
|
||||
getNodeFromPath<Value>(kclManager.ast, pathToNode, 'CallExpression')
|
||||
)
|
||||
const _err1 = nodes.find(err)
|
||||
if (err(_err1)) return _err1
|
||||
|
||||
const isAllTooltips = nodes.every((meta) => {
|
||||
if (err(meta)) return false
|
||||
return (
|
||||
meta.node?.type === 'CallExpression' &&
|
||||
toolTips.includes(meta.node.callee.name as any)
|
||||
const _nodes = paths.map((pathToNode) => {
|
||||
const tmp = getNodeFromPath<CallExpression>(
|
||||
kclManager.ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(tmp)) return tmp
|
||||
return tmp.stopAtNode
|
||||
})
|
||||
const nodes: (CallExpression | null)[] = []
|
||||
for (const node of _nodes) {
|
||||
if (err(node)) return node
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
const isAllTooltips = nodes.every((node) => {
|
||||
if (err(node)) return false
|
||||
return node && toolTips.includes(node.callee.name as any)
|
||||
})
|
||||
|
||||
const transforms = getTransformInfos(
|
||||
|
@ -186,6 +186,7 @@ export function useSetupEngineManager(
|
||||
}
|
||||
|
||||
function getDimensions(streamWidth?: number, streamHeight?: number) {
|
||||
const factorOf = 4
|
||||
const maxResolution = 2000
|
||||
const width = streamWidth ? streamWidth : 0
|
||||
const height = streamHeight ? streamHeight : 0
|
||||
@ -193,7 +194,7 @@ function getDimensions(streamWidth?: number, streamHeight?: number) {
|
||||
Math.min(maxResolution / width, maxResolution / height),
|
||||
1.0
|
||||
)
|
||||
const quadWidth = Math.round((width * ratio) / 4) * 4
|
||||
const quadHeight = Math.round((height * ratio) / 4) * 4
|
||||
const quadWidth = Math.round((width * ratio) / factorOf) * factorOf
|
||||
const quadHeight = Math.round((height * ratio) / factorOf) * factorOf
|
||||
return { width: quadWidth, height: quadHeight }
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { executeAst, lintAst } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { KCLError, kclErrorsToDiagnostics } from './errors'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { isArray, uuidv4 } from 'lib/utils'
|
||||
import { EngineCommandManager } from './std/engineConnection'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
@ -15,12 +15,17 @@ import {
|
||||
recast,
|
||||
SourceRange,
|
||||
} from 'lang/wasm'
|
||||
import { getNodeFromPath } from './queryAst'
|
||||
import {
|
||||
expectNodeOnPath,
|
||||
getLastNodeFromPath,
|
||||
getNodeFromPath,
|
||||
} from './queryAst'
|
||||
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
|
||||
export class KclManager {
|
||||
private _ast: Program = {
|
||||
type: 'Program',
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
@ -156,6 +161,7 @@ export class KclManager {
|
||||
|
||||
clearAst() {
|
||||
this._ast = {
|
||||
type: 'Program',
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
@ -224,6 +230,7 @@ export class KclManager {
|
||||
|
||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||
defaultSelectionFilter(programMemory, this.engineCommandManager)
|
||||
await this.engineCommandManager.waitForAllCommands()
|
||||
|
||||
if (zoomToFit) {
|
||||
let zoomObjectId: string | undefined = ''
|
||||
@ -243,6 +250,15 @@ export class KclManager {
|
||||
padding: 0.1, // padding around the objects
|
||||
},
|
||||
})
|
||||
await this.engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'zoom_to_fit',
|
||||
object_ids: zoomObjectId ? [zoomObjectId] : [], // leave empty to zoom to all objects
|
||||
padding: 0.1, // padding around the objects
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
this.isExecuting = false
|
||||
@ -303,14 +319,14 @@ export class KclManager {
|
||||
Object.entries(this.engineCommandManager.artifactMap).forEach(
|
||||
([commandId, artifact]) => {
|
||||
if (!artifact.pathToNode) return
|
||||
const _node1 = getNodeFromPath<CallExpression>(
|
||||
const _node = getNodeFromPath<CallExpression>(
|
||||
this.ast,
|
||||
artifact.pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(_node1)) return
|
||||
const { node } = _node1
|
||||
if (node.type !== 'CallExpression') return
|
||||
if (err(_node)) return
|
||||
const { node } = _node
|
||||
if (isArray(node) || node.type !== 'CallExpression') return
|
||||
const [oldStart, oldEnd] = artifact.range
|
||||
if (oldStart === 0 && oldEnd === 0) return
|
||||
if (oldStart === node.start && oldEnd === node.end) return
|
||||
@ -382,12 +398,15 @@ export class KclManager {
|
||||
let returnVal: Selections | undefined = undefined
|
||||
|
||||
if (optionalParams?.focusPath) {
|
||||
const _node1 = getNodeFromPath<any>(
|
||||
const _node1 = getLastNodeFromPath(
|
||||
astWithUpdatedSource,
|
||||
optionalParams?.focusPath
|
||||
)
|
||||
if (err(_node1)) return Promise.reject(_node1)
|
||||
const { node } = _node1
|
||||
if (isArray(node)) {
|
||||
return Promise.reject(new Error('Expected node to not be an array'))
|
||||
}
|
||||
|
||||
const { start, end } = node
|
||||
if (!start || !end)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
||||
import { isArray } from 'lib/utils'
|
||||
import { getNodePathFromSourceRange, getLastNodeFromPath } from './queryAst'
|
||||
import { Identifier, parse, initPromise, Parameter } from './wasm'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
@ -25,10 +26,13 @@ const sk3 = startSketchAt([0, 0])
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const _node = getNodeFromPath<any>(ast, nodePath)
|
||||
const _node = getLastNodeFromPath(ast, nodePath)
|
||||
if (err(_node)) throw _node
|
||||
const { node } = _node
|
||||
|
||||
if (isArray(node)) {
|
||||
throw new Error('Expected call expression node, but found array')
|
||||
}
|
||||
expect([node.start, node.end]).toEqual(sourceRange)
|
||||
expect(node.type).toBe('CallExpression')
|
||||
})
|
||||
@ -53,7 +57,7 @@ const b1 = cube([0,0], 10)`
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const _node = getNodeFromPath<Parameter>(ast, nodePath)
|
||||
const _node = getLastNodeFromPath(ast, nodePath)
|
||||
if (err(_node)) throw _node
|
||||
const node = _node.node
|
||||
|
||||
@ -66,8 +70,12 @@ const b1 = cube([0,0], 10)`
|
||||
['params', 'FunctionExpression'],
|
||||
[0, 'index'],
|
||||
])
|
||||
if (isArray(node)) {
|
||||
throw new Error('Expected parameter node, but found array')
|
||||
}
|
||||
expect(node.type).toBe('Parameter')
|
||||
expect(node.identifier.name).toBe('pos')
|
||||
const param = node as any as Parameter
|
||||
expect(param.identifier.name).toBe('pos')
|
||||
})
|
||||
it('gets path right for deep within function definition body', () => {
|
||||
const code = `fn cube = (pos, scale) => {
|
||||
@ -90,7 +98,7 @@ const b1 = cube([0,0], 10)`
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const _node = getNodeFromPath<Identifier>(ast, nodePath)
|
||||
const _node = getLastNodeFromPath(ast, nodePath)
|
||||
if (err(_node)) throw _node
|
||||
const node = _node.node
|
||||
expect(nodePath).toEqual([
|
||||
@ -112,7 +120,11 @@ const b1 = cube([0,0], 10)`
|
||||
['elements', 'ArrayExpression'],
|
||||
[0, 'index'],
|
||||
])
|
||||
if (isArray(node)) {
|
||||
throw new Error('Expected identifier node, but found array')
|
||||
}
|
||||
expect(node.type).toBe('Identifier')
|
||||
expect(node.name).toBe('scale')
|
||||
const ident = node as any as Identifier
|
||||
expect(ident.name).toBe('scale')
|
||||
})
|
||||
})
|
||||
|
@ -119,6 +119,7 @@ describe('Testing addSketchTo', () => {
|
||||
it('should add a sketch to a program', () => {
|
||||
const result = addSketchTo(
|
||||
{
|
||||
type: 'Program',
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
|
@ -27,6 +27,8 @@ import {
|
||||
getNodePathFromSourceRange,
|
||||
isNodeSafeToReplace,
|
||||
traverse,
|
||||
getLastNodeFromPath,
|
||||
expectNodeOnPath,
|
||||
} from './queryAst'
|
||||
import { addTagForSketchOnFace, getConstraintInfo } from './std/sketch'
|
||||
import {
|
||||
@ -36,7 +38,7 @@ import {
|
||||
transformAstSketchLines,
|
||||
} from './std/sketchcombos'
|
||||
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
|
||||
import { isOverlap, roundOff } from 'lib/utils'
|
||||
import { isArray, isOverlap, roundOff } from 'lib/utils'
|
||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
||||
import { ConstrainInfo } from './std/stdTypes'
|
||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||
@ -79,16 +81,12 @@ export function addStartProfileAt(
|
||||
pathToNode: PathToNode,
|
||||
at: [number, number]
|
||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
||||
const _node1 = getNodeFromPath<VariableDeclaration>(
|
||||
const variableDeclaration = expectNodeOnPath<VariableDeclaration>(
|
||||
node,
|
||||
pathToNode,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(_node1)) return _node1
|
||||
const variableDeclaration = _node1.node
|
||||
if (variableDeclaration.type !== 'VariableDeclaration') {
|
||||
return new Error('variableDeclaration.init.type !== PipeExpression')
|
||||
}
|
||||
if (err(variableDeclaration)) return variableDeclaration
|
||||
const _node = { ...node }
|
||||
const init = variableDeclaration.declarations[0].init
|
||||
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
||||
@ -263,7 +261,7 @@ export function extrudeSketch(
|
||||
}
|
||||
| Error {
|
||||
const _node = { ...node }
|
||||
const _node1 = getNodeFromPath(_node, pathToNode)
|
||||
const _node1 = getLastNodeFromPath(_node, pathToNode)
|
||||
if (err(_node1)) return _node1
|
||||
const { node: sketchExpression } = _node1
|
||||
|
||||
@ -274,9 +272,9 @@ export function extrudeSketch(
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(_node2)) return _node2
|
||||
const { node: pipeExpression } = _node2
|
||||
const { stopAtNode: pipeExpression } = _node2
|
||||
|
||||
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
||||
const isInPipeExpression = !!pipeExpression
|
||||
|
||||
const _node3 = getNodeFromPath<VariableDeclarator>(
|
||||
_node,
|
||||
@ -284,7 +282,11 @@ export function extrudeSketch(
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(_node3)) return _node3
|
||||
const { node: variableDeclarator, shallowPath: pathToDecleration } = _node3
|
||||
const { stopAtNode: variableDeclarator, shallowPath: pathToDecleration } =
|
||||
_node3
|
||||
if (!variableDeclarator) {
|
||||
return new Error('VariableDeclarator not found')
|
||||
}
|
||||
|
||||
const extrudeCall = createCallExpressionStdLib('extrude', [
|
||||
distance,
|
||||
@ -356,34 +358,34 @@ export function sketchOnExtrudedFace(
|
||||
node,
|
||||
KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH
|
||||
)
|
||||
const _node1 = getNodeFromPath<VariableDeclarator>(
|
||||
const oldSketchNode = expectNodeOnPath<VariableDeclarator>(
|
||||
_node,
|
||||
sketchPathToNode,
|
||||
'VariableDeclarator',
|
||||
true
|
||||
{
|
||||
firstFound: true,
|
||||
message: 'Old sketch node not found',
|
||||
}
|
||||
)
|
||||
if (err(_node1)) return _node1
|
||||
const { node: oldSketchNode } = _node1
|
||||
if (err(oldSketchNode)) return oldSketchNode
|
||||
|
||||
const oldSketchName = oldSketchNode.id.name
|
||||
const _node2 = getNodeFromPath<CallExpression>(
|
||||
const expression = expectNodeOnPath<CallExpression>(
|
||||
_node,
|
||||
sketchPathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(_node2)) return _node2
|
||||
const { node: expression } = _node2
|
||||
if (err(expression)) return expression
|
||||
|
||||
const _node3 = getNodeFromPath<VariableDeclarator>(
|
||||
const extrudeVarDec = expectNodeOnPath<VariableDeclarator>(
|
||||
_node,
|
||||
extrudePathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(_node3)) return _node3
|
||||
const { node: extrudeVarDec } = _node3
|
||||
const extrudeName = extrudeVarDec.id?.name
|
||||
if (err(extrudeVarDec)) return extrudeVarDec
|
||||
const extrudeName = extrudeVarDec.id.name
|
||||
|
||||
let _tag = null
|
||||
let _tag: Identifier | Literal | null = null
|
||||
if (cap === 'none') {
|
||||
const __tag = addTagForSketchOnFace(
|
||||
{
|
||||
@ -678,9 +680,12 @@ export function giveSketchFnCallTag(
|
||||
}
|
||||
| Error {
|
||||
const path = getNodePathFromSourceRange(ast, range)
|
||||
const _node1 = getNodeFromPath<CallExpression>(ast, path, 'CallExpression')
|
||||
if (err(_node1)) return _node1
|
||||
const { node: primaryCallExp } = _node1
|
||||
const primaryCallExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
path,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(primaryCallExp)) return primaryCallExp
|
||||
|
||||
// Tag is always 3rd expression now, using arg index feels brittle
|
||||
// but we can come up with a better way to identify tag later.
|
||||
@ -784,27 +789,35 @@ export function deleteSegmentFromPipeExpression(
|
||||
): Program | Error {
|
||||
let _modifiedAst = structuredClone(modifiedAst)
|
||||
|
||||
dependentRanges.forEach((range) => {
|
||||
for (const range of dependentRanges) {
|
||||
const path = getNodePathFromSourceRange(_modifiedAst, range)
|
||||
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const _callExp = getNodeFromPath<CallExpression>(
|
||||
_modifiedAst,
|
||||
path,
|
||||
'CallExpression',
|
||||
true
|
||||
)
|
||||
if (err(callExp)) return callExp
|
||||
if (err(_callExp)) return _callExp
|
||||
const callExp = _callExp.stopAtNode
|
||||
if (!callExp) {
|
||||
return new Error('Call Expression not found')
|
||||
}
|
||||
|
||||
const constraintInfo = getConstraintInfo(callExp.node, code, path).find(
|
||||
const constraintInfo = getConstraintInfo(callExp, code, path).find(
|
||||
({ sourceRange }) => isOverlap(sourceRange, range)
|
||||
)
|
||||
if (!constraintInfo) return
|
||||
if (!constraintInfo) {
|
||||
return new Error('Constraint Info not found')
|
||||
}
|
||||
|
||||
const input = makeRemoveSingleConstraintInput(
|
||||
constraintInfo.argPosition,
|
||||
callExp.shallowPath
|
||||
_callExp.shallowPath
|
||||
)
|
||||
if (!input) return
|
||||
if (!input) {
|
||||
return new Error('Input not found')
|
||||
}
|
||||
const transform = removeSingleConstraintInfo(
|
||||
{
|
||||
...input,
|
||||
@ -812,11 +825,13 @@ export function deleteSegmentFromPipeExpression(
|
||||
_modifiedAst,
|
||||
programMemory
|
||||
)
|
||||
if (!transform) return
|
||||
if (!transform) {
|
||||
return new Error('Transform not found')
|
||||
}
|
||||
_modifiedAst = transform.modifiedAst
|
||||
})
|
||||
}
|
||||
|
||||
const pipeExpression = getNodeFromPath<PipeExpression>(
|
||||
const pipeExpression = expectNodeOnPath<PipeExpression>(
|
||||
_modifiedAst,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
@ -827,7 +842,7 @@ export function deleteSegmentFromPipeExpression(
|
||||
([_, desc]) => desc === 'PipeExpression'
|
||||
)
|
||||
const segmentIndexInPipe = pathToNode[pipeInPathIndex + 1]
|
||||
pipeExpression.node.body.splice(segmentIndexInPipe[0] as number, 1)
|
||||
pipeExpression.body.splice(segmentIndexInPipe[0] as number, 1)
|
||||
|
||||
// Move up to the next segment.
|
||||
segmentIndexInPipe[0] = Math.max((segmentIndexInPipe[0] as number) - 1, 0)
|
||||
@ -902,19 +917,23 @@ export async function deleteFromSelection(
|
||||
const astClone = structuredClone(ast)
|
||||
const range = selection.range
|
||||
const path = getNodePathFromSourceRange(ast, range)
|
||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||
const _varDec = getNodeFromPath<VariableDeclarator>(
|
||||
ast,
|
||||
path,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(varDec)) return varDec
|
||||
if (err(_varDec)) return _varDec
|
||||
const { stopAtNode: varDec } = _varDec
|
||||
if (!varDec) {
|
||||
return new Error('VariableDeclarator not found')
|
||||
}
|
||||
if (
|
||||
(selection.type === 'extrude-wall' ||
|
||||
selection.type === 'end-cap' ||
|
||||
selection.type === 'start-cap') &&
|
||||
varDec.node.init.type === 'PipeExpression'
|
||||
varDec.init.type === 'PipeExpression'
|
||||
) {
|
||||
const varDecName = varDec.node.id.name
|
||||
const varDecName = varDec.id.name
|
||||
let pathToNode: PathToNode | null = null
|
||||
let extrudeNameToDelete = ''
|
||||
traverse(astClone, {
|
||||
@ -976,13 +995,16 @@ export async function deleteFromSelection(
|
||||
lastKey: number
|
||||
}[] = []
|
||||
for (const { path, sketchName } of pathsDependingOnExtrude) {
|
||||
const parent = getNodeFromPath<PipeExpression['body']>(
|
||||
astClone,
|
||||
path.slice(0, -1)
|
||||
)
|
||||
if (err(parent)) {
|
||||
const _parent = getLastNodeFromPath(astClone, path.slice(0, -1))
|
||||
if (err(_parent)) {
|
||||
return
|
||||
}
|
||||
const { node: parent } = _parent
|
||||
if (!isArray(parent)) {
|
||||
console.error(`Parent is not an array: ${parent}`)
|
||||
return
|
||||
}
|
||||
const pipeBodyItems = parent as PipeExpression['body']
|
||||
const sketchToPreserve = programMemory.get(sketchName) as SketchGroup
|
||||
console.log('sketchName', sketchName)
|
||||
// Can't kick off multiple requests at once as getFaceDetails
|
||||
@ -1000,7 +1022,7 @@ export async function deleteFromSelection(
|
||||
}
|
||||
const lastKey = Number(path.slice(-1)[0][0])
|
||||
modificationDetails.push({
|
||||
parent: parent.node,
|
||||
parent: pipeBodyItems,
|
||||
faceDetails,
|
||||
lastKey,
|
||||
})
|
||||
@ -1048,14 +1070,14 @@ export async function deleteFromSelection(
|
||||
}
|
||||
// await prom
|
||||
return astClone
|
||||
} else if (varDec.node.init.type === 'PipeExpression') {
|
||||
const pipeBody = varDec.node.init.body
|
||||
} else if (varDec.init.type === 'PipeExpression') {
|
||||
const pipeBody = varDec.init.body
|
||||
if (
|
||||
pipeBody[0].type === 'CallExpression' &&
|
||||
pipeBody[0].callee.name === 'startSketchOn'
|
||||
) {
|
||||
// remove varDec
|
||||
const varDecIndex = varDec.shallowPath[1][0] as number
|
||||
const varDecIndex = _varDec.shallowPath[1][0] as number
|
||||
astClone.body.splice(varDecIndex, 1)
|
||||
return astClone
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
hasValidFilletSelection,
|
||||
isTagUsedInFillet,
|
||||
} from './addFillet'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||
import { expectNodeOnPath, getNodePathFromSourceRange } from '../queryAst'
|
||||
import { createLiteral } from 'lang/modifyAst'
|
||||
import { err } from 'lib/trap'
|
||||
import { Selections } from 'lib/selections'
|
||||
@ -270,13 +270,13 @@ const extrude001 = extrude(-5, sketch001)
|
||||
]
|
||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||
if (err(pathToNode)) return
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInFillet({ ast, callExp })
|
||||
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
||||
})
|
||||
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
||||
@ -289,13 +289,13 @@ const extrude001 = extrude(-5, sketch001)
|
||||
]
|
||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||
if (err(pathToNode)) return
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInFillet({ ast, callExp })
|
||||
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
||||
})
|
||||
it('should correctly identify no edges', () => {
|
||||
@ -308,13 +308,13 @@ const extrude001 = extrude(-5, sketch001)
|
||||
]
|
||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||
if (err(pathToNode)) return
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInFillet({ ast, callExp })
|
||||
expect(edges).toEqual([])
|
||||
})
|
||||
})
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
createPipeExpression,
|
||||
} from '../modifyAst'
|
||||
import {
|
||||
expectNodeOnPath,
|
||||
getNodeFromPath,
|
||||
getNodePathFromSourceRange,
|
||||
hasSketchPipeBeenExtruded,
|
||||
@ -46,15 +47,12 @@ export function addFillet(
|
||||
*/
|
||||
|
||||
// Find the specific sketch segment to tag with the new tag
|
||||
const sketchSegmentChunk = getNodeFromPath(
|
||||
const sketchSegmentNode = expectNodeOnPath<CallExpression>(
|
||||
_node,
|
||||
pathToSegmentNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(sketchSegmentChunk)) return sketchSegmentChunk
|
||||
const { node: sketchSegmentNode } = sketchSegmentChunk as {
|
||||
node: CallExpression
|
||||
}
|
||||
if (err(sketchSegmentNode)) return sketchSegmentNode
|
||||
|
||||
// Check whether selection is a valid segment from sketchLineHelpersMap
|
||||
if (!(sketchSegmentNode.callee.name in sketchLineHelperMap)) {
|
||||
@ -93,13 +91,12 @@ export function addFillet(
|
||||
])
|
||||
|
||||
// Locate the extrude call
|
||||
const extrudeChunk = getNodeFromPath<VariableDeclaration>(
|
||||
const extrudeVarDecl = expectNodeOnPath<VariableDeclaration>(
|
||||
_node,
|
||||
pathToExtrudeNode,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(extrudeChunk)) return extrudeChunk
|
||||
const { node: extrudeVarDecl } = extrudeChunk
|
||||
if (err(extrudeVarDecl)) return extrudeVarDecl
|
||||
|
||||
const extrudeDeclarator = extrudeVarDecl.declarations[0]
|
||||
const extrudeInit = extrudeDeclarator.init
|
||||
@ -281,12 +278,12 @@ export const hasValidFilletSelection = ({
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(segmentNode)) return false
|
||||
if (segmentNode.node.type === 'CallExpression') {
|
||||
const segmentName = segmentNode.node.callee.name
|
||||
if (segmentNode.stopAtNode) {
|
||||
const segmentName = segmentNode.stopAtNode.callee.name
|
||||
if (segmentName in sketchLineHelperMap) {
|
||||
const edges = isTagUsedInFillet({
|
||||
ast,
|
||||
callExp: segmentNode.node,
|
||||
callExp: segmentNode.stopAtNode,
|
||||
})
|
||||
// edge has already been filleted
|
||||
if (
|
||||
|
@ -19,13 +19,47 @@ import {
|
||||
} from './wasm'
|
||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
|
||||
import { getAngle } from '../lib/utils'
|
||||
import { getAngle, isArray } from '../lib/utils'
|
||||
import { getFirstArg } from './std/sketch'
|
||||
import {
|
||||
getConstraintLevelFromSourceRange,
|
||||
getConstraintType,
|
||||
} from './std/sketchcombos'
|
||||
import { err } from 'lib/trap'
|
||||
import { BodyItem } from 'wasm-lib/kcl/bindings/BodyItem'
|
||||
|
||||
export interface DynamicNode {
|
||||
type: SyntaxType
|
||||
// Source range of the node.
|
||||
start: number
|
||||
end: number
|
||||
[index: string]: unknown
|
||||
}
|
||||
|
||||
function isAstNode(
|
||||
node: any
|
||||
): node is { type: SyntaxType; start: number; end: number } {
|
||||
// TODO: Should we check for start and end also?
|
||||
return node && typeof node === 'object' && 'type' in node
|
||||
}
|
||||
|
||||
/**
|
||||
* Given T and its corresponding SyntaxType, narrow the node to type T if
|
||||
* node.type matches.
|
||||
*/
|
||||
export function isNodeType<T extends DynamicNode>(
|
||||
node: unknown,
|
||||
syntaxType: SyntaxType | SyntaxType[]
|
||||
): node is T {
|
||||
return (
|
||||
!!node &&
|
||||
typeof node === 'object' &&
|
||||
'type' in node &&
|
||||
(isArray(syntaxType)
|
||||
? syntaxType.includes(node.type as SyntaxType)
|
||||
: node.type === syntaxType)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
||||
@ -33,51 +67,64 @@ import { err } from 'lib/trap'
|
||||
* and return the node at the end of this path.
|
||||
* By default it will return the node of the deepest "stopAt" type encountered, or the node at the end of the path if no "stopAt" type is provided.
|
||||
* If the "returnEarly" flag is set to true, the function will return as soon as a node of the specified type is found.
|
||||
*
|
||||
* If stopAt is provided, it must match T's type property.
|
||||
*/
|
||||
export function getNodeFromPath<T>(
|
||||
export function getNodeFromPath<T extends DynamicNode>(
|
||||
node: Program,
|
||||
path: PathToNode,
|
||||
stopAt?: SyntaxType | SyntaxType[],
|
||||
returnEarly = false
|
||||
):
|
||||
| {
|
||||
node: T
|
||||
node: DynamicNode | unknown[]
|
||||
stopAtNode: T | null
|
||||
shallowPath: PathToNode
|
||||
deepPath: PathToNode
|
||||
}
|
||||
| Error {
|
||||
let currentNode = node as any
|
||||
let stopAtNode = null
|
||||
let currentNode: DynamicNode | unknown[] = node
|
||||
let stopAtNode: T | null = null
|
||||
let successfulPaths: PathToNode = []
|
||||
let pathsExplored: PathToNode = []
|
||||
for (const pathItem of path) {
|
||||
if (typeof currentNode[pathItem[0]] !== 'object') {
|
||||
if (stopAtNode) {
|
||||
const pathIndex = pathItem[0]
|
||||
let nextNode: unknown
|
||||
if (currentNode && typeof pathIndex === 'number' && isArray(currentNode)) {
|
||||
nextNode = currentNode[pathIndex]
|
||||
}
|
||||
if (
|
||||
currentNode &&
|
||||
typeof pathIndex === 'string' &&
|
||||
typeof currentNode === 'object' &&
|
||||
!isArray(currentNode)
|
||||
) {
|
||||
nextNode = currentNode[pathIndex]
|
||||
}
|
||||
if (!isArray(nextNode) && !isAstNode(nextNode)) {
|
||||
if (isAstNode(stopAtNode)) {
|
||||
return {
|
||||
node: stopAtNode,
|
||||
stopAtNode,
|
||||
shallowPath: pathsExplored,
|
||||
deepPath: successfulPaths,
|
||||
}
|
||||
}
|
||||
return new Error('not an object')
|
||||
}
|
||||
currentNode = currentNode?.[pathItem[0]]
|
||||
currentNode = nextNode
|
||||
successfulPaths.push(pathItem)
|
||||
if (!stopAtNode) {
|
||||
pathsExplored.push(pathItem)
|
||||
}
|
||||
if (
|
||||
typeof stopAt !== 'undefined' &&
|
||||
(Array.isArray(stopAt)
|
||||
? stopAt.includes(currentNode.type)
|
||||
: currentNode.type === stopAt)
|
||||
) {
|
||||
if (stopAt && isNodeType<T>(currentNode, stopAt)) {
|
||||
// it will match the deepest node of the type
|
||||
// instead of returning at the first match
|
||||
stopAtNode = currentNode
|
||||
if (returnEarly) {
|
||||
return {
|
||||
node: stopAtNode,
|
||||
stopAtNode,
|
||||
shallowPath: pathsExplored,
|
||||
deepPath: successfulPaths,
|
||||
}
|
||||
@ -86,32 +133,104 @@ export function getNodeFromPath<T>(
|
||||
}
|
||||
return {
|
||||
node: stopAtNode || currentNode,
|
||||
stopAtNode,
|
||||
shallowPath: pathsExplored,
|
||||
deepPath: successfulPaths,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the terminal node in the given path. Like getNodeFromPath, but no
|
||||
* stopAt parameter.
|
||||
*/
|
||||
export function getLastNodeFromPath(
|
||||
node: Program,
|
||||
path: PathToNode
|
||||
):
|
||||
| {
|
||||
node: DynamicNode | unknown[]
|
||||
}
|
||||
| Error {
|
||||
const _result = getNodeFromPath<DynamicNode>(node, path)
|
||||
if (err(_result)) return _result
|
||||
return {
|
||||
node: _result.node,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the terminal node in the given path, and asserts that it's the given
|
||||
* type.
|
||||
*/
|
||||
export function expectLastNodeFromPath<T extends DynamicNode>(
|
||||
node: Program,
|
||||
path: PathToNode,
|
||||
syntaxType: SyntaxType | SyntaxType[]
|
||||
): T | Error {
|
||||
const result = getLastNodeFromPath(node, path)
|
||||
if (err(result)) return result
|
||||
if (!isNodeType<T>(result.node, syntaxType)) {
|
||||
return new Error(
|
||||
`Expected node of type ${syntaxType}: found ${JSON.stringify(result)}`
|
||||
)
|
||||
}
|
||||
|
||||
return result.node
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node in the path with the given type. Like getNodeFromPath, but
|
||||
* if no stopAt node is found, an error is returned instead of null.
|
||||
*
|
||||
* @param {boolean} [options.firstFound=false] if true, return the first node of
|
||||
* the type found. The default returns the last node found.
|
||||
* @param {boolean} [options.message] the error message to return if the node is
|
||||
* not found.
|
||||
*/
|
||||
export function expectNodeOnPath<T extends DynamicNode>(
|
||||
node: Program,
|
||||
path: PathToNode,
|
||||
syntaxType: SyntaxType,
|
||||
options: { firstFound?: boolean; message?: string } = {
|
||||
firstFound: false,
|
||||
}
|
||||
): T | Error {
|
||||
const result = getNodeFromPath<T>(node, path, syntaxType, options.firstFound)
|
||||
if (err(result)) return result
|
||||
if (!result.stopAtNode) {
|
||||
return new Error(
|
||||
options.message ?? `Node of type ${syntaxType} not found in path`
|
||||
)
|
||||
}
|
||||
return result.stopAtNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions the same as getNodeFromPath, but returns a curried function that can be called with the stopAt and returnEarly arguments.
|
||||
*/
|
||||
export function getNodeFromPathCurry(
|
||||
node: Program,
|
||||
path: PathToNode
|
||||
): <T>(
|
||||
stopAt?: SyntaxType | SyntaxType[],
|
||||
): <T extends DynamicNode>(
|
||||
stopAt: SyntaxType | SyntaxType[],
|
||||
returnEarly?: boolean
|
||||
) =>
|
||||
| {
|
||||
node: T
|
||||
node: DynamicNode | unknown[]
|
||||
stopAtNode: T | null
|
||||
path: PathToNode
|
||||
}
|
||||
| Error {
|
||||
return <T>(stopAt?: SyntaxType | SyntaxType[], returnEarly = false) => {
|
||||
return <T extends DynamicNode>(
|
||||
stopAt: SyntaxType | SyntaxType[],
|
||||
returnEarly = false
|
||||
) => {
|
||||
const _node1 = getNodeFromPath<T>(node, path, stopAt, returnEarly)
|
||||
if (err(_node1)) return _node1
|
||||
const { node: _node, shallowPath } = _node1
|
||||
const { node: _node, stopAtNode, shallowPath } = _node1
|
||||
return {
|
||||
node: _node,
|
||||
stopAtNode,
|
||||
path: shallowPath,
|
||||
}
|
||||
}
|
||||
@ -459,7 +578,7 @@ export function findAllPreviousVariablesPath(
|
||||
|
||||
const { index: insertIndex, path: bodyPath } = splitPathAtLastIndex(pathToDec)
|
||||
|
||||
const _node2 = getNodeFromPath<Program['body']>(ast, bodyPath)
|
||||
const _node2 = getLastNodeFromPath(ast, bodyPath)
|
||||
if (err(_node2)) {
|
||||
console.error(_node2)
|
||||
return {
|
||||
@ -468,19 +587,30 @@ export function findAllPreviousVariablesPath(
|
||||
insertIndex: 0,
|
||||
}
|
||||
}
|
||||
if (!isArray(_node2.node)) {
|
||||
console.error(
|
||||
`Expected node of type array, but found: ${JSON.stringify(_node2)}`
|
||||
)
|
||||
return {
|
||||
variables: [],
|
||||
bodyPath: [],
|
||||
insertIndex: 0,
|
||||
}
|
||||
}
|
||||
const { node: bodyItems } = _node2
|
||||
|
||||
const variables: PrevVariable<any>[] = []
|
||||
bodyItems?.forEach?.((item) => {
|
||||
if (item.type !== 'VariableDeclaration' || item.end > startRange) return
|
||||
for (const unknownItem of bodyItems) {
|
||||
const item = unknownItem as BodyItem
|
||||
if (item.type !== 'VariableDeclaration' || item.end > startRange) continue
|
||||
const varName = item.declarations[0].id.name
|
||||
const varValue = programMemory?.get(varName)
|
||||
if (!varValue || typeof varValue?.value !== type) return
|
||||
if (!varValue || typeof varValue?.value !== type) continue
|
||||
variables.push({
|
||||
key: varName,
|
||||
value: varValue.value,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
insertIndex,
|
||||
@ -554,7 +684,7 @@ export function isNodeSafeToReplacePath(
|
||||
}
|
||||
pathToReplaced[1][0] = index + 1
|
||||
const startPath = finPath.slice(0, -1)
|
||||
const _nodeToReplace = getNodeFromPath(_ast, startPath)
|
||||
const _nodeToReplace = getLastNodeFromPath(_ast, startPath)
|
||||
if (err(_nodeToReplace)) return _nodeToReplace
|
||||
const nodeToReplace = _nodeToReplace.node as any
|
||||
nodeToReplace[last[0]] = identifier
|
||||
@ -656,7 +786,8 @@ export function isLinesParallelAndConstrained(
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(_secondaryNode)) return _secondaryNode
|
||||
const secondaryNode = _secondaryNode.node
|
||||
const secondaryNode = _secondaryNode.stopAtNode
|
||||
if (!secondaryNode) return new Error('no secondary node found')
|
||||
const _varDec = getNodeFromPath(ast, primaryPath, 'VariableDeclaration')
|
||||
if (err(_varDec)) return _varDec
|
||||
const varDec = _varDec.node
|
||||
@ -682,7 +813,7 @@ export function isLinesParallelAndConstrained(
|
||||
Math.abs(primaryAngle - secondaryAngle) < EPSILON ||
|
||||
Math.abs(primaryAngle - secondaryAngleAlt) < EPSILON
|
||||
|
||||
// is secordary line fully constrain, or has constrain type of 'angle'
|
||||
// is secondary line fully constrain, or has constrain type of 'angle'
|
||||
const secondaryFirstArg = getFirstArg(secondaryNode)
|
||||
if (err(secondaryFirstArg)) return secondaryFirstArg
|
||||
|
||||
@ -747,8 +878,8 @@ export function doesPipeHaveCallExp({
|
||||
console.error(pipeExpressionMeta)
|
||||
return false
|
||||
}
|
||||
const pipeExpression = pipeExpressionMeta.node
|
||||
if (pipeExpression.type !== 'PipeExpression') return false
|
||||
const pipeExpression = pipeExpressionMeta.stopAtNode
|
||||
if (!pipeExpression) return false
|
||||
return pipeExpression.body.some(
|
||||
(expression) =>
|
||||
expression.type === 'CallExpression' &&
|
||||
@ -775,8 +906,8 @@ export function hasExtrudeSketchGroup({
|
||||
console.error(varDecMeta)
|
||||
return false
|
||||
}
|
||||
const varDec = varDecMeta.node
|
||||
if (varDec.type !== 'VariableDeclaration') return false
|
||||
const varDec = varDecMeta.stopAtNode
|
||||
if (!varDec) return false
|
||||
const varName = varDec.declarations[0].id.name
|
||||
const varValue = programMemory?.get(varName)
|
||||
return varValue?.type === 'ExtrudeGroup' || varValue?.type === 'SketchGroup'
|
||||
@ -814,8 +945,8 @@ export function findUsesOfTagInPipe(
|
||||
console.error(nodeMeta)
|
||||
return []
|
||||
}
|
||||
const node = nodeMeta.node
|
||||
if (node.type !== 'CallExpression') return []
|
||||
const node = nodeMeta.stopAtNode
|
||||
if (!node) return []
|
||||
const tagIndex = node.callee.name === 'close' ? 1 : 2
|
||||
const thirdParam = node.arguments[tagIndex]
|
||||
if (
|
||||
@ -836,9 +967,14 @@ export function findUsesOfTagInPipe(
|
||||
console.error(varDec)
|
||||
return []
|
||||
}
|
||||
const varDecNode = varDec.stopAtNode
|
||||
if (!varDecNode) {
|
||||
console.error('varDecNode not found')
|
||||
return []
|
||||
}
|
||||
const dependentRanges: SourceRange[] = []
|
||||
|
||||
traverse(varDec.node, {
|
||||
traverse(varDecNode, {
|
||||
enter: (node) => {
|
||||
if (
|
||||
node.type !== 'CallExpression' ||
|
||||
@ -860,16 +996,16 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
||||
const path = getNodePathFromSourceRange(ast, selection.range)
|
||||
const _node = getNodeFromPath<PipeExpression>(ast, path, 'PipeExpression')
|
||||
if (err(_node)) return false
|
||||
const { node: pipeExpression } = _node
|
||||
if (pipeExpression.type !== 'PipeExpression') return false
|
||||
const { stopAtNode: pipeExpression } = _node
|
||||
if (!pipeExpression) return false
|
||||
const _varDec = getNodeFromPath<VariableDeclarator>(
|
||||
ast,
|
||||
path,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(_varDec)) return false
|
||||
const varDec = _varDec.node
|
||||
if (varDec.type !== 'VariableDeclarator') return false
|
||||
const varDec = _varDec.stopAtNode
|
||||
if (!varDec) return false
|
||||
let extruded = false
|
||||
traverse(ast as any, {
|
||||
enter(node) {
|
||||
|
@ -14,7 +14,11 @@ import {
|
||||
SourceRange,
|
||||
CallExpression,
|
||||
} from '../wasm'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||
import {
|
||||
expectNodeOnPath,
|
||||
getNodeFromPath,
|
||||
getNodePathFromSourceRange,
|
||||
} from '../queryAst'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
@ -600,13 +604,13 @@ describe('testing getConstraintInfo', () => {
|
||||
]
|
||||
if (err(ast)) return ast
|
||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return callExp
|
||||
const result = getConstraintInfo(callExp.node, code, pathToNode)
|
||||
const result = getConstraintInfo(callExp, code, pathToNode)
|
||||
expect(result).toEqual(expected)
|
||||
})
|
||||
})
|
||||
@ -754,13 +758,13 @@ describe('testing getConstraintInfo', () => {
|
||||
]
|
||||
if (err(ast)) return ast
|
||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return callExp
|
||||
const result = getConstraintInfo(callExp.node, code, pathToNode)
|
||||
const result = getConstraintInfo(callExp, code, pathToNode)
|
||||
expect(result).toEqual(expected)
|
||||
})
|
||||
})
|
||||
@ -1110,14 +1114,14 @@ describe('testing getConstraintInfo', () => {
|
||||
]
|
||||
if (err(ast)) return ast
|
||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return callExp
|
||||
|
||||
const result = getConstraintInfo(callExp.node, code, pathToNode)
|
||||
const result = getConstraintInfo(callExp, code, pathToNode)
|
||||
expect(result).toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
@ -14,8 +14,10 @@ import {
|
||||
Identifier,
|
||||
} from 'lang/wasm'
|
||||
import {
|
||||
isNodeType,
|
||||
expectLastNodeFromPath,
|
||||
expectNodeOnPath,
|
||||
getNodeFromPath,
|
||||
getNodeFromPathCurry,
|
||||
getNodePathFromSourceRange,
|
||||
} from 'lang/queryAst'
|
||||
import {
|
||||
@ -50,7 +52,7 @@ import {
|
||||
mutateObjExpProp,
|
||||
findUniqueName,
|
||||
} from 'lang/modifyAst'
|
||||
import { roundOff, getLength, getAngle } from 'lib/utils'
|
||||
import { roundOff, getLength, getAngle, isArray } from 'lib/utils'
|
||||
import { err } from 'lib/trap'
|
||||
import { perpendicularDistance } from 'sketch-helpers'
|
||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||
@ -330,13 +332,12 @@ export const lineTo: SketchLineHelper = {
|
||||
referencedSegment,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
const pipe = expectNodeOnPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: pipe } = nodeMeta
|
||||
if (err(pipe)) return pipe
|
||||
|
||||
const newVals: [Value, Value] = [
|
||||
createLiteral(roundOff(to[0], 2)),
|
||||
@ -373,7 +374,7 @@ export const lineTo: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
|
||||
@ -382,8 +383,13 @@ export const lineTo: SketchLineHelper = {
|
||||
createLiteral(to[1]),
|
||||
])
|
||||
|
||||
mutateArrExp(callExpression.arguments?.[0], toArrExp) ||
|
||||
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
mutateArrExp(firstArg, toArrExp) ||
|
||||
mutateObjExpProp(firstArg, toArrExp, 'to')
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
pathToNode,
|
||||
@ -414,7 +420,7 @@ export const line: SketchLineHelper = {
|
||||
spliceBetween,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression | CallExpression>(
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
@ -427,12 +433,16 @@ export const line: SketchLineHelper = {
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(nodeMeta2)) return nodeMeta2
|
||||
const { node: varDec } = nodeMeta2
|
||||
const { stopAtNode: varDec } = nodeMeta2
|
||||
|
||||
const newXVal = createLiteral(roundOff(to[0] - from[0], 2))
|
||||
const newYVal = createLiteral(roundOff(to[1] - from[1], 2))
|
||||
|
||||
if (spliceBetween && !createCallback && pipe.type === 'PipeExpression') {
|
||||
if (
|
||||
spliceBetween &&
|
||||
!createCallback &&
|
||||
isNodeType<PipeExpression>(pipe, 'PipeExpression')
|
||||
) {
|
||||
const callExp = createCallExpression('line', [
|
||||
createArrayExpression([newXVal, newYVal]),
|
||||
createPipeSubstitution(),
|
||||
@ -455,7 +465,11 @@ export const line: SketchLineHelper = {
|
||||
}
|
||||
}
|
||||
|
||||
if (replaceExisting && createCallback && pipe.type !== 'CallExpression') {
|
||||
if (
|
||||
replaceExisting &&
|
||||
createCallback &&
|
||||
isNodeType<PipeExpression>(pipe, 'PipeExpression')
|
||||
) {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[newXVal, newYVal],
|
||||
@ -477,7 +491,7 @@ export const line: SketchLineHelper = {
|
||||
createArrayExpression([newXVal, newYVal]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
if (pipe.type === 'PipeExpression') {
|
||||
if (isNodeType<PipeExpression>(pipe, 'PipeExpression')) {
|
||||
pipe.body = [...pipe.body, callExp]
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -488,6 +502,7 @@ export const line: SketchLineHelper = {
|
||||
],
|
||||
}
|
||||
} else {
|
||||
if (!varDec) return new Error('Variable declaration not found')
|
||||
varDec.init = createPipeExpression([varDec.init, callExp])
|
||||
}
|
||||
return {
|
||||
@ -497,7 +512,11 @@ export const line: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
|
||||
@ -506,10 +525,15 @@ export const line: SketchLineHelper = {
|
||||
createLiteral(roundOff(to[1] - from[1], 2)),
|
||||
])
|
||||
|
||||
if (callExpression.arguments?.[0].type === 'ObjectExpression') {
|
||||
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
||||
} else {
|
||||
mutateArrExp(callExpression.arguments?.[0], toArrExp)
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
if (firstArg.type === 'ObjectExpression') {
|
||||
mutateObjExpProp(firstArg, toArrExp, 'to')
|
||||
} else {
|
||||
mutateArrExp(firstArg, toArrExp)
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -531,10 +555,12 @@ export const line: SketchLineHelper = {
|
||||
export const xLineTo: SketchLineHelper = {
|
||||
add: ({ node, pathToNode, to, replaceExisting, createCallback }) => {
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
if (err(_node1)) return _node1
|
||||
const { node: pipe } = _node1
|
||||
const pipe = expectLastNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(pipe)) return pipe
|
||||
|
||||
const newVal = createLiteral(roundOff(to[0], 2))
|
||||
|
||||
@ -563,14 +589,24 @@ export const xLineTo: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
const newX = createLiteral(roundOff(to[0], 2))
|
||||
if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) {
|
||||
callExpression.arguments[0] = newX
|
||||
} else {
|
||||
mutateObjExpProp(callExpression.arguments?.[0], newX, 'to')
|
||||
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
const newX = createLiteral(roundOff(to[0], 2))
|
||||
if (isLiteralArrayOrStatic(firstArg)) {
|
||||
callExpression.arguments[0] = newX
|
||||
} else {
|
||||
mutateObjExpProp(firstArg, newX, 'to')
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -592,10 +628,12 @@ export const xLineTo: SketchLineHelper = {
|
||||
export const yLineTo: SketchLineHelper = {
|
||||
add: ({ node, pathToNode, to, replaceExisting, createCallback }) => {
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
if (err(_node1)) return _node1
|
||||
const { node: pipe } = _node1
|
||||
const pipe = expectLastNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(pipe)) return pipe
|
||||
|
||||
const newVal = createLiteral(roundOff(to[1], 2))
|
||||
|
||||
@ -624,14 +662,24 @@ export const yLineTo: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
const newY = createLiteral(roundOff(to[1], 2))
|
||||
if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) {
|
||||
callExpression.arguments[0] = newY
|
||||
} else {
|
||||
mutateObjExpProp(callExpression.arguments?.[0], newY, 'to')
|
||||
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
const newY = createLiteral(roundOff(to[1], 2))
|
||||
if (isLiteralArrayOrStatic(firstArg)) {
|
||||
callExpression.arguments[0] = newY
|
||||
} else {
|
||||
mutateObjExpProp(firstArg, newY, 'to')
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -653,10 +701,12 @@ export const yLineTo: SketchLineHelper = {
|
||||
export const xLine: SketchLineHelper = {
|
||||
add: ({ node, pathToNode, to, from, replaceExisting, createCallback }) => {
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
if (err(_node1)) return _node1
|
||||
const { node: pipe } = _node1
|
||||
const pipe = expectNodeOnPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(pipe)) return pipe
|
||||
|
||||
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
||||
const firstArg = newVal
|
||||
@ -684,14 +734,24 @@ export const xLine: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
const newX = createLiteral(roundOff(to[0] - from[0], 2))
|
||||
if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) {
|
||||
callExpression.arguments[0] = newX
|
||||
} else {
|
||||
mutateObjExpProp(callExpression.arguments?.[0], newX, 'length')
|
||||
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
const newX = createLiteral(roundOff(to[0] - from[0], 2))
|
||||
if (isLiteralArrayOrStatic(firstArg)) {
|
||||
callExpression.arguments[0] = newX
|
||||
} else {
|
||||
mutateObjExpProp(firstArg, newX, 'length')
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -713,10 +773,12 @@ export const xLine: SketchLineHelper = {
|
||||
export const yLine: SketchLineHelper = {
|
||||
add: ({ node, pathToNode, to, from, replaceExisting, createCallback }) => {
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
if (err(_node1)) return _node1
|
||||
const { node: pipe } = _node1
|
||||
const pipe = expectNodeOnPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(pipe)) return pipe
|
||||
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
||||
if (replaceExisting && createCallback) {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
@ -741,14 +803,24 @@ export const yLine: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
const newY = createLiteral(roundOff(to[1] - from[1], 2))
|
||||
if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) {
|
||||
callExpression.arguments[0] = newY
|
||||
} else {
|
||||
mutateObjExpProp(callExpression.arguments?.[0], newY, 'length')
|
||||
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
const newY = createLiteral(roundOff(to[1] - from[1], 2))
|
||||
if (isLiteralArrayOrStatic(firstArg)) {
|
||||
callExpression.arguments[0] = newY
|
||||
} else {
|
||||
mutateObjExpProp(firstArg, newY, 'length')
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -777,8 +849,11 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
referencedSegment,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression | CallExpression>('PipeExpression')
|
||||
const _node1 = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(_node1)) return _node1
|
||||
const { node: pipe } = _node1
|
||||
const _node2 = getNodeFromPath<VariableDeclarator>(
|
||||
@ -787,12 +862,16 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(_node2)) return _node2
|
||||
const { node: varDec } = _node2
|
||||
const { stopAtNode: varDec } = _node2
|
||||
|
||||
const toX = createLiteral(roundOff(to[0], 2))
|
||||
const toY = createLiteral(roundOff(to[1], 2))
|
||||
|
||||
if (replaceExisting && createCallback && pipe.type !== 'CallExpression') {
|
||||
if (
|
||||
replaceExisting &&
|
||||
createCallback &&
|
||||
isNodeType<PipeExpression>(pipe, 'PipeExpression')
|
||||
) {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[toX, toY],
|
||||
@ -813,7 +892,7 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
createArrayExpression([toX, toY]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
if (pipe.type === 'PipeExpression') {
|
||||
if (isNodeType<PipeExpression>(pipe, 'PipeExpression')) {
|
||||
pipe.body = [...pipe.body, newLine]
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -824,6 +903,7 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
],
|
||||
}
|
||||
} else {
|
||||
if (!varDec) return new Error('Variable declaration not found')
|
||||
varDec.init = createPipeExpression([varDec.init, newLine])
|
||||
}
|
||||
return {
|
||||
@ -833,15 +913,23 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
const x = createLiteral(roundOff(to[0], 2))
|
||||
const y = createLiteral(roundOff(to[1], 2))
|
||||
|
||||
const firstArg = callExpression.arguments?.[0]
|
||||
if (!mutateArrExp(firstArg, createArrayExpression([x, y]))) {
|
||||
mutateObjExpProp(firstArg, createArrayExpression([x, y]), 'to')
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
if (!mutateArrExp(firstArg, createArrayExpression([x, y]))) {
|
||||
mutateObjExpProp(firstArg, createArrayExpression([x, y]), 'to')
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -909,10 +997,12 @@ export const angledLine: SketchLineHelper = {
|
||||
referencedSegment,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
if (err(_node1)) return _node1
|
||||
const { node: pipe } = _node1
|
||||
const pipe = expectNodeOnPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(pipe)) return pipe
|
||||
|
||||
const newAngleVal = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
const newLengthVal = createLiteral(roundOff(getLength(from, to), 2))
|
||||
@ -947,7 +1037,11 @@ export const angledLine: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
const angle = roundOff(getAngle(from, to), 0)
|
||||
@ -956,10 +1050,16 @@ export const angledLine: SketchLineHelper = {
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(lineLength)
|
||||
|
||||
const firstArg = callExpression.arguments?.[0]
|
||||
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'length')
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
if (
|
||||
!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))
|
||||
) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'length')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@ -993,20 +1093,18 @@ export const angledLineOfXLength: SketchLineHelper = {
|
||||
replaceExisting,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
const pipe = expectNodeOnPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: pipe } = nodeMeta
|
||||
const nodeMeta2 = getNodeFromPath<VariableDeclarator>(
|
||||
if (err(pipe)) return pipe
|
||||
const varDec = expectNodeOnPath<VariableDeclarator>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(nodeMeta2)) return nodeMeta2
|
||||
const { node: varDec } = nodeMeta2
|
||||
if (err(varDec)) return varDec
|
||||
|
||||
const variableName = varDec.id.name
|
||||
const sketch = previousProgramMemory?.get(variableName)
|
||||
@ -1040,23 +1138,33 @@ export const angledLineOfXLength: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
const angle = roundOff(getAngle(from, to), 0)
|
||||
const xLength = roundOff(Math.abs(to[0] - from[0]), 2)
|
||||
|
||||
const firstArg = callExpression.arguments?.[0]
|
||||
const adjustedXLength = isAngleLiteral(firstArg)
|
||||
? Math.abs(xLength)
|
||||
: xLength // todo make work for variable angle > 180
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
const adjustedXLength = isAngleLiteral(firstArg)
|
||||
? Math.abs(xLength)
|
||||
: xLength // todo make work for variable angle > 180
|
||||
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(adjustedXLength)
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(adjustedXLength)
|
||||
|
||||
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'length')
|
||||
if (
|
||||
!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))
|
||||
) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'length')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@ -1090,20 +1198,18 @@ export const angledLineOfYLength: SketchLineHelper = {
|
||||
replaceExisting,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
const pipe = expectNodeOnPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: pipe } = nodeMeta
|
||||
const nodeMeta2 = getNodeFromPath<VariableDeclarator>(
|
||||
if (err(pipe)) return pipe
|
||||
const varDec = expectNodeOnPath<VariableDeclarator>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(nodeMeta2)) return nodeMeta2
|
||||
const { node: varDec } = nodeMeta2
|
||||
if (err(varDec)) return varDec
|
||||
const variableName = varDec.id.name
|
||||
const sketch = previousProgramMemory?.get(variableName)
|
||||
if (!sketch || sketch.type !== 'SketchGroup') {
|
||||
@ -1137,23 +1243,33 @@ export const angledLineOfYLength: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node: callExpression } = nodeMeta
|
||||
const angle = roundOff(getAngle(from, to), 0)
|
||||
const yLength = roundOff(to[1] - from[1], 2)
|
||||
|
||||
const firstArg = callExpression.arguments?.[0]
|
||||
const adjustedYLength = isAngleLiteral(firstArg)
|
||||
? Math.abs(yLength)
|
||||
: yLength // todo make work for variable angle > 180
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
const adjustedYLength = isAngleLiteral(firstArg)
|
||||
? Math.abs(yLength)
|
||||
: yLength // todo make work for variable angle > 180
|
||||
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(adjustedYLength)
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(adjustedYLength)
|
||||
|
||||
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'length')
|
||||
if (
|
||||
!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))
|
||||
) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'length')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@ -1187,14 +1303,13 @@ export const angledLineToX: SketchLineHelper = {
|
||||
referencedSegment,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
const pipe = expectNodeOnPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
if (err(pipe)) return pipe
|
||||
|
||||
const { node: pipe } = nodeMeta
|
||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
const xArg = createLiteral(roundOff(to[0], 2))
|
||||
if (replaceExisting && createCallback) {
|
||||
@ -1227,22 +1342,32 @@ export const angledLineToX: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
|
||||
const { node: callExpression } = nodeMeta
|
||||
|
||||
const angle = roundOff(getAngle(from, to), 0)
|
||||
const xLength = roundOff(to[0], 2)
|
||||
|
||||
const firstArg = callExpression.arguments?.[0]
|
||||
const adjustedXLength = xLength
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
const adjustedXLength = xLength
|
||||
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(adjustedXLength)
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(adjustedXLength)
|
||||
|
||||
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'to')
|
||||
if (
|
||||
!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))
|
||||
) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'to')
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -1275,14 +1400,12 @@ export const angledLineToY: SketchLineHelper = {
|
||||
referencedSegment,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
const pipe = expectNodeOnPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
|
||||
const { node: pipe } = nodeMeta
|
||||
if (err(pipe)) return pipe
|
||||
|
||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
const yArg = createLiteral(roundOff(to[1], 2))
|
||||
@ -1317,22 +1440,32 @@ export const angledLineToY: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
|
||||
const { node: callExpression } = nodeMeta
|
||||
|
||||
const angle = roundOff(getAngle(from, to), 0)
|
||||
const xLength = roundOff(to[1], 2)
|
||||
|
||||
const firstArg = callExpression.arguments?.[0]
|
||||
const adjustedXLength = xLength
|
||||
if (isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
const firstArg = callExpression.arguments[0]
|
||||
if (firstArg) {
|
||||
const adjustedXLength = xLength
|
||||
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(adjustedXLength)
|
||||
const angleLit = createLiteral(angle)
|
||||
const lengthLit = createLiteral(adjustedXLength)
|
||||
|
||||
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'to')
|
||||
if (
|
||||
!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))
|
||||
) {
|
||||
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||
mutateObjExpProp(firstArg, lengthLit, 'to')
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -1372,7 +1505,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
|
||||
const { node: pipe } = nodeMeta
|
||||
const { stopAtNode: pipe } = nodeMeta
|
||||
|
||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
if (!referencedSegment) {
|
||||
@ -1409,6 +1542,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
]
|
||||
)
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
if (!pipe) return new Error('Pipe expression not found')
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -1420,10 +1554,13 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from, previousProgramMemory }) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const callExpression = expectLastNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExpression)) return callExpression
|
||||
|
||||
const { node: callExpression } = nodeMeta
|
||||
const angle = roundOff(getAngle(from, to), 0)
|
||||
|
||||
const firstArg = callExpression.arguments?.[0]
|
||||
@ -1434,14 +1571,13 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
: createLiteral('')
|
||||
const intersectTagName =
|
||||
intersectTag.type === 'Identifier' ? intersectTag.name : ''
|
||||
const nodeMeta2 = getNodeFromPath<VariableDeclaration>(
|
||||
const varDec = expectNodeOnPath<VariableDeclaration>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(nodeMeta2)) return nodeMeta2
|
||||
if (err(varDec)) return varDec
|
||||
|
||||
const { node: varDec } = nodeMeta2
|
||||
const varName = varDec.declarations[0].id.name
|
||||
const sketchGroup = previousProgramMemory.get(varName) as SketchGroup
|
||||
const intersectPath = sketchGroup.value.find(
|
||||
@ -1553,11 +1689,16 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||
to,
|
||||
}) => {
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) {
|
||||
console.error(nodeMeta)
|
||||
const callExpression = expectLastNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExpression)) {
|
||||
console.error(callExpression)
|
||||
return {
|
||||
modifiedAst: {
|
||||
type: 'Program',
|
||||
start: 0,
|
||||
end: 0,
|
||||
body: [],
|
||||
@ -1572,7 +1713,6 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||
}
|
||||
}
|
||||
|
||||
const { node: callExpression } = nodeMeta
|
||||
const toArrExp = createArrayExpression([
|
||||
createLiteral(roundOff(to[0])),
|
||||
createLiteral(roundOff(to[1])),
|
||||
@ -1615,8 +1755,14 @@ export function changeSketchArguments(
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
|
||||
const { node: callExpression, shallowPath } = nodeMeta
|
||||
if (isArray(callExpression)) {
|
||||
return new Error('Expected call expression but found array')
|
||||
}
|
||||
if (!isNodeType<CallExpression>(callExpression, 'CallExpression')) {
|
||||
return new Error('Call expression not found')
|
||||
}
|
||||
|
||||
if (callExpression?.callee?.name in sketchLineHelperMap) {
|
||||
if (callExpression.callee.name in sketchLineHelperMap) {
|
||||
const { updateArgs } = sketchLineHelperMap[callExpression.callee.name]
|
||||
if (!updateArgs) {
|
||||
return new Error('not a sketch line helper')
|
||||
@ -1631,7 +1777,7 @@ export function changeSketchArguments(
|
||||
})
|
||||
}
|
||||
|
||||
return new Error(`not a sketch line helper: ${callExpression?.callee?.name}`)
|
||||
return new Error(`not a sketch line helper: ${callExpression.callee.name}`)
|
||||
}
|
||||
|
||||
export function getConstraintInfo(
|
||||
@ -1737,10 +1883,13 @@ export function addCallExpressionsToPipe({
|
||||
)
|
||||
if (err(pipeExpression)) return pipeExpression
|
||||
|
||||
if (pipeExpression.node.type !== 'PipeExpression') {
|
||||
if (!pipeExpression.stopAtNode) {
|
||||
return new Error('not a pipe expression')
|
||||
}
|
||||
pipeExpression.node.body = [...pipeExpression.node.body, ...expressions]
|
||||
pipeExpression.stopAtNode.body = [
|
||||
...pipeExpression.stopAtNode.body,
|
||||
...expressions,
|
||||
]
|
||||
return _node
|
||||
}
|
||||
|
||||
@ -1763,10 +1912,13 @@ export function addCloseToPipe({
|
||||
)
|
||||
if (err(pipeExpression)) return pipeExpression
|
||||
|
||||
if (pipeExpression.node.type !== 'PipeExpression') {
|
||||
if (!pipeExpression.stopAtNode) {
|
||||
return new Error('not a pipe expression')
|
||||
}
|
||||
pipeExpression.node.body = [...pipeExpression.node.body, closeExpression]
|
||||
pipeExpression.stopAtNode.body = [
|
||||
...pipeExpression.stopAtNode.body,
|
||||
closeExpression,
|
||||
]
|
||||
return _node
|
||||
}
|
||||
|
||||
@ -1854,18 +2006,16 @@ type addTagFn = (a: AddTagInfo) => { modifiedAst: Program; tag: string } | Error
|
||||
function addTag(tagIndex = 2): addTagFn {
|
||||
return ({ node, pathToNode }) => {
|
||||
const _node = { ...node }
|
||||
const callExpr = getNodeFromPath<CallExpression>(
|
||||
const primaryCallExp = expectNodeOnPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExpr)) return callExpr
|
||||
|
||||
const { node: primaryCallExp } = callExpr
|
||||
if (err(primaryCallExp)) return primaryCallExp
|
||||
|
||||
// Tag is always 3rd expression now, using arg index feels brittle
|
||||
// but we can come up with a better way to identify tag later.
|
||||
const thirdArg = primaryCallExp.arguments?.[tagIndex]
|
||||
const thirdArg = primaryCallExp.arguments[tagIndex]
|
||||
const tagDeclarator =
|
||||
thirdArg ||
|
||||
(createTagDeclarator(findUniqueName(_node, 'seg', 2)) as TagDeclarator)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getNodeFromPath } from 'lang/queryAst'
|
||||
import { getLastNodeFromPath } from 'lang/queryAst'
|
||||
import { ToolTip, toolTips } from 'lang/langHelpers'
|
||||
import {
|
||||
Program,
|
||||
@ -8,9 +8,9 @@ import {
|
||||
SourceRange,
|
||||
Path,
|
||||
PathToNode,
|
||||
Value,
|
||||
} from '../wasm'
|
||||
import { err } from 'lib/trap'
|
||||
import { isArray } from 'lib/utils'
|
||||
|
||||
export function getSketchSegmentFromPathToNode(
|
||||
sketchGroup: SketchGroup,
|
||||
@ -25,11 +25,14 @@ export function getSketchSegmentFromPathToNode(
|
||||
// TODO: once pathTodNode is stored on program memory as part of execution,
|
||||
// we can check if the pathToNode matches the pathToNode of the sketchGroup.
|
||||
// For now we fall back to the sourceRange
|
||||
const nodeMeta = getNodeFromPath<Value>(ast, pathToNode)
|
||||
const nodeMeta = getLastNodeFromPath(ast, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
const { node } = nodeMeta
|
||||
if (isArray(node)) {
|
||||
return new Error('Value node expected, but found array')
|
||||
}
|
||||
|
||||
const node = nodeMeta.node
|
||||
if (!node || typeof node.start !== 'number' || !node.end)
|
||||
if (typeof node.start !== 'number' || !node.end)
|
||||
return new Error('no node found')
|
||||
const sourceRange: SourceRange = [node.start, node.end]
|
||||
return getSketchSegmentFromSourceRange(sketchGroup, sourceRange)
|
||||
|
@ -12,6 +12,9 @@ import {
|
||||
ProgramMemory,
|
||||
} from '../wasm'
|
||||
import {
|
||||
isNodeType,
|
||||
expectNodeOnPath,
|
||||
getLastNodeFromPath,
|
||||
getNodeFromPath,
|
||||
getNodeFromPathCurry,
|
||||
getNodePathFromSourceRange,
|
||||
@ -39,7 +42,7 @@ import {
|
||||
getSketchSegmentFromPathToNode,
|
||||
getSketchSegmentFromSourceRange,
|
||||
} from './sketchConstraints'
|
||||
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
|
||||
import { getAngle, roundOff, normaliseAngle, isArray } from '../../lib/utils'
|
||||
|
||||
export type LineInputsType =
|
||||
| 'xAbsolute'
|
||||
@ -1225,22 +1228,19 @@ export function removeSingleConstraint({
|
||||
objectProperty?: string
|
||||
ast: Program
|
||||
}): TransformInfo | false {
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
pathToCallExp,
|
||||
'CallExpression'
|
||||
'CallExpression',
|
||||
{ message: 'Invalid node type' }
|
||||
)
|
||||
if (err(callExp)) {
|
||||
console.error(callExp)
|
||||
return false
|
||||
}
|
||||
if (callExp.node.type !== 'CallExpression') {
|
||||
console.error(new Error('Invalid node type'))
|
||||
return false
|
||||
}
|
||||
|
||||
const transform: TransformInfo = {
|
||||
tooltip: callExp.node.callee.name as any,
|
||||
tooltip: callExp.callee.name as any,
|
||||
createNode:
|
||||
({ tag, referenceSegName, varValues }) =>
|
||||
(_, rawValues) => {
|
||||
@ -1264,7 +1264,7 @@ export function removeSingleConstraint({
|
||||
})
|
||||
const objExp = createObjectExpression(expression)
|
||||
return createStdlibCallExpression(
|
||||
callExp.node.callee.name as any,
|
||||
callExp.callee.name as any,
|
||||
objExp,
|
||||
tag
|
||||
)
|
||||
@ -1289,7 +1289,7 @@ export function removeSingleConstraint({
|
||||
return varValue.value
|
||||
})
|
||||
return createStdlibCallExpression(
|
||||
callExp.node.callee.name as any,
|
||||
callExp.callee.name as any,
|
||||
createArrayExpression(values),
|
||||
tag
|
||||
)
|
||||
@ -1298,7 +1298,7 @@ export function removeSingleConstraint({
|
||||
// if (typeof arrayIndex !== 'number' || !objectProperty) must be single value input xLine, yLineTo etc
|
||||
|
||||
return createCallWrapper(
|
||||
callExp.node.callee.name as any,
|
||||
callExp.callee.name as any,
|
||||
rawValues[0].value,
|
||||
tag
|
||||
)
|
||||
@ -1416,7 +1416,7 @@ export function getTransformInfos(
|
||||
getNodePathFromSourceRange(ast, range)
|
||||
)
|
||||
const nodes = paths.map((pathToNode) =>
|
||||
getNodeFromPath<Value>(ast, pathToNode, 'CallExpression')
|
||||
getNodeFromPath<CallExpression>(ast, pathToNode, 'CallExpression')
|
||||
)
|
||||
|
||||
try {
|
||||
@ -1426,9 +1426,8 @@ export function getTransformInfos(
|
||||
return false
|
||||
}
|
||||
|
||||
const node = nodeMeta.node
|
||||
if (node?.type === 'CallExpression')
|
||||
return getTransformInfo(node, constraintType)
|
||||
const node = nodeMeta.stopAtNode
|
||||
if (node) return getTransformInfo(node, constraintType)
|
||||
|
||||
return false
|
||||
}) as TransformInfo[]
|
||||
@ -1448,9 +1447,7 @@ export function getRemoveConstraintsTransforms(
|
||||
const paths = selectionRanges.codeBasedSelections.map((selectionRange) =>
|
||||
getNodePathFromSourceRange(ast, selectionRange.range)
|
||||
)
|
||||
const nodes = paths.map((pathToNode) =>
|
||||
getNodeFromPath<Value>(ast, pathToNode)
|
||||
)
|
||||
const nodes = paths.map((pathToNode) => getLastNodeFromPath(ast, pathToNode))
|
||||
|
||||
const theTransforms = nodes.map((nodeMeta) => {
|
||||
// Typescript is not smart enough to know node will never be Error
|
||||
@ -1461,7 +1458,11 @@ export function getRemoveConstraintsTransforms(
|
||||
}
|
||||
|
||||
const node = nodeMeta.node
|
||||
if (node?.type === 'CallExpression')
|
||||
if (isArray(node)) {
|
||||
console.error('Expected node, but found Array')
|
||||
return false
|
||||
}
|
||||
if (isNodeType<CallExpression>(node, 'CallExpression'))
|
||||
return getRemoveConstraintsTransform(node, constraintType)
|
||||
|
||||
return false
|
||||
@ -1579,15 +1580,17 @@ export function transformAstSketchLines({
|
||||
|
||||
const callExp = getNode<CallExpression>('CallExpression')
|
||||
if (err(callExp)) return callExp
|
||||
if (!callExp.stopAtNode) return new Error('Call expression not found')
|
||||
const varDec = getNode<VariableDeclarator>('VariableDeclarator')
|
||||
if (err(varDec)) return varDec
|
||||
if (!varDec.stopAtNode) return new Error('Variable declaration not found')
|
||||
|
||||
const firstArg = getFirstArg(callExp.node)
|
||||
const firstArg = getFirstArg(callExp.stopAtNode)
|
||||
if (err(firstArg)) return firstArg
|
||||
const callBackTag = callExp.node.arguments[2]
|
||||
const callBackTag = callExp.stopAtNode.arguments[2]
|
||||
const _referencedSegmentNameVal =
|
||||
callExp.node.arguments[0]?.type === 'ObjectExpression' &&
|
||||
callExp.node.arguments[0].properties?.find(
|
||||
callExp.stopAtNode.arguments[0]?.type === 'ObjectExpression' &&
|
||||
callExp.stopAtNode.arguments[0].properties?.find(
|
||||
(prop) => prop.key.name === 'intersectTag'
|
||||
)?.value
|
||||
const _referencedSegmentName =
|
||||
@ -1601,7 +1604,7 @@ export function transformAstSketchLines({
|
||||
|
||||
const varValues: VarValues = []
|
||||
|
||||
getConstraintInfo(callExp.node, '', _pathToNode).forEach((a) => {
|
||||
getConstraintInfo(callExp.stopAtNode, '', _pathToNode).forEach((a) => {
|
||||
if (
|
||||
a.type === 'tangentialWithPrevious' ||
|
||||
a.type === 'horizontal' ||
|
||||
@ -1609,33 +1612,46 @@ export function transformAstSketchLines({
|
||||
)
|
||||
return
|
||||
|
||||
const nodeMeta = getNodeFromPath<Value>(ast, a.pathToNode)
|
||||
const nodeMeta = getLastNodeFromPath(ast, a.pathToNode)
|
||||
if (err(nodeMeta)) return
|
||||
|
||||
// TODO: Assert that the node is a valid value.
|
||||
if (a?.argPosition?.type === 'arrayItem') {
|
||||
if (isArray(nodeMeta.node)) {
|
||||
console.log('Expected Value, but found Array')
|
||||
return
|
||||
}
|
||||
varValues.push({
|
||||
type: 'arrayItem',
|
||||
index: a.argPosition.index,
|
||||
value: nodeMeta.node,
|
||||
value: nodeMeta.node as Value,
|
||||
argType: a.type,
|
||||
})
|
||||
} else if (a?.argPosition?.type === 'objectProperty') {
|
||||
if (isArray(nodeMeta.node)) {
|
||||
console.log('Expected Value, but found Array')
|
||||
return
|
||||
}
|
||||
varValues.push({
|
||||
type: 'objectProperty',
|
||||
key: a.argPosition.key,
|
||||
value: nodeMeta.node,
|
||||
value: nodeMeta.node as Value,
|
||||
argType: a.type,
|
||||
})
|
||||
} else if (a?.argPosition?.type === 'singleValue') {
|
||||
if (isArray(nodeMeta.node)) {
|
||||
console.log('Expected Value, but found Array')
|
||||
return
|
||||
}
|
||||
varValues.push({
|
||||
type: 'singleValue',
|
||||
argType: a.type,
|
||||
value: nodeMeta.node,
|
||||
value: nodeMeta.node as Value,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const varName = varDec.node.id.name
|
||||
const varName = varDec.stopAtNode.id.name
|
||||
let sketchGroup = programMemory.get(varName)
|
||||
if (sketchGroup?.type === 'ExtrudeGroup') {
|
||||
sketchGroup = sketchGroup.sketchGroup
|
||||
@ -1669,7 +1685,7 @@ export function transformAstSketchLines({
|
||||
programMemory,
|
||||
pathToNode: _pathToNode,
|
||||
referencedSegment,
|
||||
fnName: transformTo || (callExp.node.callee.name as ToolTip),
|
||||
fnName: transformTo || (callExp.stopAtNode.callee.name as ToolTip),
|
||||
to,
|
||||
from,
|
||||
createCallback: callBack({
|
||||
@ -1743,15 +1759,14 @@ export function getConstraintLevelFromSourceRange(
|
||||
ast: Program | Error
|
||||
): Error | { range: [number, number]; level: ConstraintLevel } {
|
||||
if (err(ast)) return ast
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
const sketchFnExp = expectNodeOnPath<CallExpression>(
|
||||
ast,
|
||||
getNodePathFromSourceRange(ast, cursorRange),
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
if (err(sketchFnExp)) return sketchFnExp
|
||||
|
||||
const { node: sketchFnExp } = nodeMeta
|
||||
const name = sketchFnExp?.callee?.name as ToolTip
|
||||
const name = sketchFnExp.callee.name as ToolTip
|
||||
const range: [number, number] = [sketchFnExp.start, sketchFnExp.end]
|
||||
if (!toolTips.includes(name)) return { level: 'free', range: range }
|
||||
|
||||
|
@ -5,15 +5,16 @@ import {
|
||||
kclManager,
|
||||
sceneEntitiesManager,
|
||||
} from 'lib/singletons'
|
||||
import { CallExpression, SourceRange, Value, parse, recast } from 'lang/wasm'
|
||||
import { CallExpression, SourceRange, parse, recast } from 'lang/wasm'
|
||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { isArray, uuidv4 } from 'lib/utils'
|
||||
import { EditorSelection, SelectionRange } from '@codemirror/state'
|
||||
import { getNormalisedCoordinates, isOverlap } from 'lib/utils'
|
||||
import { isCursorInSketchCommandRange } from 'lang/util'
|
||||
import { Program } from 'lang/wasm'
|
||||
import {
|
||||
doesPipeHaveCallExp,
|
||||
getLastNodeFromPath,
|
||||
getNodeFromPath,
|
||||
hasSketchPipeBeenExtruded,
|
||||
isSingleCursorInPipe,
|
||||
@ -177,7 +178,11 @@ export function getEventForSegmentSelection(
|
||||
)
|
||||
if (err(nodeMeta)) return null
|
||||
|
||||
const node = nodeMeta.node
|
||||
const node = nodeMeta.stopAtNode
|
||||
if (!node) {
|
||||
console.error('Call expression not found')
|
||||
return null
|
||||
}
|
||||
const range: SourceRange = [node.start, node.end]
|
||||
return {
|
||||
type: 'Set selection',
|
||||
@ -300,7 +305,11 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(nodeMeta)) return
|
||||
const node = nodeMeta.node
|
||||
const node = nodeMeta.stopAtNode
|
||||
if (!node) {
|
||||
console.error('Call expression not found')
|
||||
return
|
||||
}
|
||||
const groupHasCursor = codeBasedSelections.some((selection) => {
|
||||
return isOverlap(selection.range, [node.start, node.end])
|
||||
})
|
||||
@ -597,9 +606,10 @@ export function updateSelections(
|
||||
|
||||
const newSelections = Object.entries(pathToNodeMap)
|
||||
.map(([index, pathToNode]): Selection | undefined => {
|
||||
const nodeMeta = getNodeFromPath<Value>(ast, pathToNode)
|
||||
const nodeMeta = getLastNodeFromPath(ast, pathToNode)
|
||||
if (err(nodeMeta)) return undefined
|
||||
const node = nodeMeta.node
|
||||
if (isArray(node)) return undefined
|
||||
return {
|
||||
range: [node.start, node.end],
|
||||
type: prevSelectionRanges.codeBasedSelections[Number(index)]?.type,
|
||||
|
@ -4,6 +4,13 @@ import { v4 } from 'uuid'
|
||||
|
||||
export const uuidv4 = v4
|
||||
|
||||
/**
|
||||
* A safer type guard for arrays since the built-in Array.isArray() asserts `any[]`.
|
||||
*/
|
||||
export function isArray(val: any): val is unknown[] {
|
||||
return Array.isArray(val)
|
||||
}
|
||||
|
||||
export function isOverlap(a: SourceRange, b: SourceRange) {
|
||||
const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a]
|
||||
const [lastOfFirst, firstOfSecond] = [startingRange[1], secondRange[0]]
|
||||
|
@ -11,6 +11,7 @@ import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
|
||||
import {
|
||||
isNodeSafeToReplacePath,
|
||||
getNodePathFromSourceRange,
|
||||
expectNodeOnPath,
|
||||
} from 'lang/queryAst'
|
||||
import {
|
||||
kclManager,
|
||||
@ -879,8 +880,8 @@ export const modelingMachine = createMachine(
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(variableDeclaration)) return false
|
||||
if (variableDeclaration.node.type !== 'VariableDeclarator') return false
|
||||
const pipeExpression = variableDeclaration.node.init
|
||||
if (!variableDeclaration.stopAtNode) return false
|
||||
const pipeExpression = variableDeclaration.stopAtNode.init
|
||||
if (pipeExpression.type !== 'PipeExpression') return false
|
||||
const hasStartSketchOn = pipeExpression.body.some(
|
||||
(item) =>
|
||||
@ -1082,7 +1083,9 @@ export const modelingMachine = createMachine(
|
||||
},
|
||||
})
|
||||
if (!engineCommandManager.engineConnection?.idleMode) {
|
||||
store.videoElement?.play()
|
||||
store.videoElement?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e)
|
||||
})
|
||||
}
|
||||
if (updatedAst?.selections) {
|
||||
editorManager.selectRange(updatedAst?.selections)
|
||||
@ -1132,13 +1135,13 @@ export const modelingMachine = createMachine(
|
||||
selection.codeBasedSelections[0].range
|
||||
)
|
||||
|
||||
const varDecNode = getNodeFromPath<VariableDeclaration>(
|
||||
const varDecNode = expectNodeOnPath<VariableDeclaration>(
|
||||
ast,
|
||||
pathToSegmentNode,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(varDecNode)) return
|
||||
const sketchVar = varDecNode.node.declarations[0].id.name
|
||||
const sketchVar = varDecNode.declarations[0].id.name
|
||||
const sketchGroup = kclManager.programMemory.get(sketchVar)
|
||||
if (sketchGroup?.type !== 'SketchGroup') return
|
||||
const idArtifact = engineCommandManager.artifactMap[sketchGroup.id]
|
||||
@ -1623,14 +1626,13 @@ export function isEditingExistingSketch({
|
||||
// should check that the variable declaration is a pipeExpression
|
||||
// and that the pipeExpression contains a "startProfileAt" callExpression
|
||||
if (!sketchDetails?.sketchPathToNode) return false
|
||||
const variableDeclaration = getNodeFromPath<VariableDeclarator>(
|
||||
const variableDeclaration = expectNodeOnPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
sketchDetails.sketchPathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(variableDeclaration)) return false
|
||||
if (variableDeclaration.node.type !== 'VariableDeclarator') return false
|
||||
const pipeExpression = variableDeclaration.node.init
|
||||
const pipeExpression = variableDeclaration.init
|
||||
if (pipeExpression.type !== 'PipeExpression') return false
|
||||
const hasStartProfileAt = pipeExpression.body.some(
|
||||
(item) =>
|
||||
@ -1652,5 +1654,5 @@ export function canRectangleTool({
|
||||
// This should not be returning false, and it should be caught
|
||||
// but we need to simulate old behavior to move on.
|
||||
if (err(node)) return false
|
||||
return node.node?.declarations?.[0]?.init.type !== 'PipeExpression'
|
||||
return node.stopAtNode?.declarations?.[0]?.init.type !== 'PipeExpression'
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
# Each test can have at most 4 threads, but if its name contains "serial_test_", then it
|
||||
# also requires 4 threads.
|
||||
# This means such tests run one at a time, with 4 threads.
|
||||
|
||||
[test-groups]
|
||||
serial-integration = { max-threads = 4 }
|
||||
# If a test uses the engine, we want to limit the number that can run in parallel.
|
||||
# This way we don't start and stop too many engine instances, putting pressure on our cloud.
|
||||
uses-engine = { max-threads = 4 }
|
||||
|
||||
[profile.default]
|
||||
slow-timeout = { period = "30s", terminate-after = 1 }
|
||||
@ -12,13 +10,15 @@ slow-timeout = { period = "30s", terminate-after = 1 }
|
||||
slow-timeout = { period = "50s", terminate-after = 5 }
|
||||
|
||||
[[profile.default.overrides]]
|
||||
filter = "test(serial_test_)"
|
||||
test-group = "serial-integration"
|
||||
# If a test starts with kcl_test_, then it uses the engine. So, limit its parallelism.
|
||||
filter = "test(kcl_test_)"
|
||||
test-group = "uses-engine"
|
||||
threads-required = 2
|
||||
|
||||
[[profile.ci.overrides]]
|
||||
filter = "test(serial_test_)"
|
||||
test-group = "serial-integration"
|
||||
# If a test starts with kcl_test_, then it uses the engine. So, limit its parallelism.
|
||||
filter = "test(kcl_test_)"
|
||||
test-group = "uses-engine"
|
||||
threads-required = 2
|
||||
|
||||
[[profile.default.overrides]]
|
||||
|
30
src/wasm-lib/Cargo.lock
generated
30
src/wasm-lib/Cargo.lock
generated
@ -406,9 +406,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.11"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
|
||||
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -416,9 +416,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.11"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
|
||||
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -430,9 +430,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.11"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
|
||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@ -3128,9 +3128,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.16"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@ -3140,24 +3140,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.7"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.17"
|
||||
version = "0.22.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
dependencies = [
|
||||
"indexmap 2.2.5",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.6.5",
|
||||
"winnow 0.6.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3851,9 +3851,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.5"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -11,13 +11,13 @@ crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
bson = { version = "2.11.0", features = ["uuid-1", "chrono"] }
|
||||
clap = "4.5.11"
|
||||
clap = "4.5.13"
|
||||
gloo-utils = "0.2.0"
|
||||
kcl-lib = { path = "kcl" }
|
||||
kittycad.workspace = true
|
||||
serde_json = "1.0.121"
|
||||
tokio = { version = "1.39.2", features = ["sync"] }
|
||||
toml = "0.8.16"
|
||||
toml = "0.8.19"
|
||||
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.91"
|
||||
wasm-bindgen-futures = "0.4.42"
|
||||
|
@ -766,85 +766,10 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn #test_name() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
|
||||
// Create the client.
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
// Set a local engine address if it's set.
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer(#code_block).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default()).await.unwrap();
|
||||
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
|
||||
// Ensure it lints.
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
// Zoom to fit.
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await.unwrap();
|
||||
|
||||
// Send a snapshot request to the engine.
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await.unwrap();
|
||||
|
||||
// Create a temporary file to write the output to.
|
||||
let output_file = std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
// Save the snapshot locally.
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
|
||||
// Read the output file.
|
||||
let actual = image::io::Reader::open(output_file).unwrap().decode().unwrap();
|
||||
twenty_twenty::assert_image(&format!("tests/outputs/{}.png", #test_name_str), &actual, 0.99);
|
||||
|
||||
let code = #code_block;
|
||||
// Note, `crate` must be kcl_lib
|
||||
let result = crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(&format!("tests/outputs/{}.png", #test_name_str), &result, 0.99);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_someFn {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_someFn0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("someFn()").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "someFn()";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_someFn {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_someFn0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("someFn()").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "someFn()";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_show {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_show0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is another code block.\nyes sirrr.\nshow";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
@ -116,76 +54,14 @@ mod test_examples_show {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_show1() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nshow";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show1"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_show {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_show0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nshow";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -22,77 +22,14 @@ mod test_examples_my_func {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_my_func0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens =
|
||||
crate::token::lexer("This is another code block.\nyes sirrr.\nmyFunc").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is another code block.\nyes sirrr.\nmyFunc";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_my_func0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
@ -118,76 +55,14 @@ mod test_examples_my_func {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_my_func1() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmyFunc").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nmyFunc";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_my_func1"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -22,77 +22,14 @@ mod test_examples_line_to {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_line_to0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens =
|
||||
crate::token::lexer("This is another code block.\nyes sirrr.\nlineTo").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is another code block.\nyes sirrr.\nlineTo";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_line_to0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
@ -118,76 +55,14 @@ mod test_examples_line_to {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_line_to1() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nlineTo").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nlineTo";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_line_to1"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_min {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_min0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmin").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is another code block.\nyes sirrr.\nmin";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_min0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
@ -116,76 +54,14 @@ mod test_examples_min {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_min1() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmin").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nmin";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_min1"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_show {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_show0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nshow";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_import {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_import0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nimport";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_import {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_import0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nimport";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_import {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_import0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nimport";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -21,76 +21,14 @@ mod test_examples_show {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn serial_test_example_show0() {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.run(&program, None).await.unwrap();
|
||||
let results = program.lint_all().unwrap();
|
||||
if !results.is_empty() {
|
||||
panic!("Linting failed: {:?}", results);
|
||||
}
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::ZoomToFit {
|
||||
object_ids: Default::default(),
|
||||
padding: 0.1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
crate::executor::SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::TakeSnapshot {
|
||||
format: kittycad::types::ImageFormat::Png,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let output_file =
|
||||
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
||||
} = &resp
|
||||
{
|
||||
std::fs::write(&output_file, &data.contents.0).unwrap();
|
||||
} else {
|
||||
panic!("Unexpected response from engine: {:?}", resp);
|
||||
}
|
||||
|
||||
let actual = image::io::Reader::open(output_file)
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
let code = "This is code.\nIt does other shit.\nshow";
|
||||
let result =
|
||||
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
|
||||
.await
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&actual,
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ fn basic() {
|
||||
digest: None,
|
||||
})],
|
||||
non_code_meta: NonCodeMeta::default(),
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
};
|
||||
assert_eq!(expected, actual);
|
||||
|
@ -16,7 +16,7 @@ async-recursion = "1.1.1"
|
||||
async-trait = "0.1.81"
|
||||
base64 = "0.22.1"
|
||||
chrono = "0.4.38"
|
||||
clap = { version = "4.5.11", default-features = false, optional = true }
|
||||
clap = { version = "4.5.13", default-features = false, optional = true }
|
||||
convert_case = "0.6.0"
|
||||
dashmap = "6.0.1"
|
||||
databake = { version = "0.1.8", features = ["derive"] }
|
||||
@ -25,6 +25,7 @@ form_urlencoded = "1.2.1"
|
||||
futures = { version = "0.3.30" }
|
||||
git_rev = "0.1.0"
|
||||
gltf-json = "1.4.1"
|
||||
image = { version = "0.25.1", default-features = false, features = ["png"] }
|
||||
kittycad = { workspace = true, features = ["clap"] }
|
||||
lazy_static = "1.5.0"
|
||||
mime_guess = "2.0.5"
|
||||
@ -38,7 +39,7 @@ serde_json = "1.0.121"
|
||||
sha2 = "0.10.8"
|
||||
tabled = { version = "0.15.0", optional = true }
|
||||
thiserror = "1.0.63"
|
||||
toml = "0.8.16"
|
||||
toml = "0.8.19"
|
||||
ts-rs = { version = "9.0.1", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] }
|
||||
url = { version = "2.5.2", features = ["serde"] }
|
||||
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
|
||||
|
@ -41,6 +41,17 @@ pub type Digest = [u8; 32];
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Program {
|
||||
// This is so that it can be used with other AST nodes in TypeScript and the
|
||||
// node type can be determined at runtime.
|
||||
#[serde(
|
||||
default,
|
||||
rename = "type",
|
||||
serialize_with = "Program::serialize_ts_type",
|
||||
skip_deserializing
|
||||
)]
|
||||
#[ts(rename = "type", type = "\"Program\"")]
|
||||
pub ts_type: (),
|
||||
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
pub body: Vec<BodyItem>,
|
||||
@ -82,6 +93,13 @@ impl Program {
|
||||
hasher.update(slf.non_code_meta.compute_digest());
|
||||
});
|
||||
|
||||
fn serialize_ts_type<S>(_: &(), serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str("Program")
|
||||
}
|
||||
|
||||
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||
// Check if we are in the non code meta.
|
||||
if let Some(meta) = self.get_non_code_meta_for_position(pos) {
|
||||
@ -5695,6 +5713,7 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
|
||||
end: 0,
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
},
|
||||
return_type: None,
|
||||
@ -5723,6 +5742,7 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
|
||||
end: 0,
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
},
|
||||
return_type: None,
|
||||
@ -5751,6 +5771,7 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
|
||||
end: 0,
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
},
|
||||
return_type: None,
|
||||
@ -5792,6 +5813,7 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
|
||||
end: 0,
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
},
|
||||
return_type: None,
|
||||
|
@ -2910,6 +2910,7 @@ let w = f() + f()
|
||||
end: 0,
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
},
|
||||
return_type: None,
|
||||
|
@ -26,6 +26,7 @@ pub mod lsp;
|
||||
pub mod parser;
|
||||
pub mod settings;
|
||||
pub mod std;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod test_server;
|
||||
pub mod thread;
|
||||
pub mod token;
|
||||
|
@ -872,6 +872,7 @@ pub fn function_body(i: TokenSlice) -> PResult<Program> {
|
||||
end,
|
||||
body,
|
||||
non_code_meta,
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
})
|
||||
}
|
||||
@ -1746,6 +1747,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
||||
}],
|
||||
digest: None,
|
||||
},
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
},
|
||||
return_type: None,
|
||||
@ -2400,6 +2402,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
||||
digest: None,
|
||||
})],
|
||||
non_code_meta: NonCodeMeta::default(),
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
};
|
||||
|
||||
@ -2864,6 +2867,7 @@ e
|
||||
digest: None,
|
||||
})],
|
||||
non_code_meta: NonCodeMeta::default(),
|
||||
ts_type: (),
|
||||
digest: None,
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,70 @@
|
||||
//! Types used to send data to the test server.
|
||||
|
||||
use crate::{
|
||||
executor::{ExecutorContext, ExecutorSettings},
|
||||
settings::types::UnitLength,
|
||||
};
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub struct RequestBody {
|
||||
pub kcl_program: String,
|
||||
#[serde(default)]
|
||||
pub test_name: String,
|
||||
}
|
||||
|
||||
/// Executes a kcl program and takes a snapshot of the result.
|
||||
/// This returns the bytes of the snapshot.
|
||||
pub async fn execute_and_snapshot(code: &str, units: UnitLength) -> anyhow::Result<image::DynamicImage> {
|
||||
let ctx = new_context(units).await?;
|
||||
let tokens = crate::token::lexer(code)?;
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast()?;
|
||||
|
||||
let snapshot = ctx.execute_and_prepare_snapshot(&program).await?;
|
||||
|
||||
// Create a temporary file to write the output to.
|
||||
let output_file = std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
||||
// Save the snapshot locally, to that temporary file.
|
||||
std::fs::write(&output_file, snapshot.contents.0)?;
|
||||
// Decode the snapshot, return it.
|
||||
let img = image::io::Reader::open(output_file).unwrap().decode()?;
|
||||
Ok(img)
|
||||
}
|
||||
|
||||
async fn new_context(units: UnitLength) -> anyhow::Result<ExecutorContext> {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
|
||||
// Create the client.
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
// Set a local engine address if it's set.
|
||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let ctx = ExecutorContext::new(
|
||||
&client,
|
||||
ExecutorSettings {
|
||||
units,
|
||||
highlight_edges: true,
|
||||
enable_ssao: false,
|
||||
show_grid: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(ctx)
|
||||
}
|
||||
|
9
src/wasm-lib/tests/executor/inputs/angled_line.kcl
Normal file
9
src/wasm-lib/tests/executor/inputs/angled_line.kcl
Normal file
@ -0,0 +1,9 @@
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([4.83, 12.56], %)
|
||||
|> line([15.1, 2.48], %)
|
||||
|> line([3.15, -9.85], %, $seg01)
|
||||
|> line([-15.17, -4.1], %)
|
||||
|> angledLine([segAng(seg01), 12.35], %)
|
||||
|> line([-13.02, 10.03], %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)
|
9
src/wasm-lib/tests/executor/inputs/close_arc.kcl
Normal file
9
src/wasm-lib/tests/executor/inputs/close_arc.kcl
Normal file
@ -0,0 +1,9 @@
|
||||
const center = [0,0]
|
||||
const radius = 40
|
||||
const height = 3
|
||||
|
||||
const body = startSketchOn('XY')
|
||||
|> startProfileAt([center[0]+radius, center[1]], %)
|
||||
|> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
|
||||
|> close(%)
|
||||
|> extrude(height, %)
|
7
src/wasm-lib/tests/executor/inputs/dimensions_match.kcl
Normal file
7
src/wasm-lib/tests/executor/inputs/dimensions_match.kcl
Normal file
@ -0,0 +1,7 @@
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(10, %)
|
4
src/wasm-lib/tests/executor/inputs/helix_ccw.kcl
Normal file
4
src/wasm-lib/tests/executor/inputs/helix_ccw.kcl
Normal file
@ -0,0 +1,4 @@
|
||||
const part001 = startSketchOn('XY')
|
||||
|> circle([5, 5], 10, %)
|
||||
|> extrude(10, %)
|
||||
|> helix({revolutions: 16, angle_start: 0, ccw: true}, %)
|
4
src/wasm-lib/tests/executor/inputs/helix_defaults.kcl
Normal file
4
src/wasm-lib/tests/executor/inputs/helix_defaults.kcl
Normal file
@ -0,0 +1,4 @@
|
||||
const part001 = startSketchOn('XY')
|
||||
|> circle([5, 5], 10, %)
|
||||
|> extrude(10, %)
|
||||
|> helix({revolutions: 16, angle_start: 0}, %)
|
@ -0,0 +1,4 @@
|
||||
const part001 = startSketchOn('XY')
|
||||
|> circle([5, 5], 10, %)
|
||||
|> extrude(-10, %)
|
||||
|> helix({revolutions: 16, angle_start: 0}, %)
|
4
src/wasm-lib/tests/executor/inputs/helix_with_length.kcl
Normal file
4
src/wasm-lib/tests/executor/inputs/helix_with_length.kcl
Normal file
@ -0,0 +1,4 @@
|
||||
const part001 = startSketchOn('XY')
|
||||
|> circle([5, 5], 10, %)
|
||||
|> extrude(10, %)
|
||||
|> helix({revolutions: 16, angle_start: 0, length: 3}, %)
|
@ -0,0 +1,17 @@
|
||||
fn cube = (pos, scale) => {
|
||||
const sg = startSketchOn('XY')
|
||||
|> startProfileAt(pos, %)
|
||||
|> line([0, scale], %)
|
||||
|> line([scale, 0], %)
|
||||
|> line([0, -scale], %)
|
||||
|> close(%)
|
||||
|
||||
return sg
|
||||
}
|
||||
|
||||
const b1 = cube([0,0], 10)
|
||||
const b2 = cube([3,3], 4)
|
||||
|> extrude(10, %)
|
||||
|
||||
const pt1 = b1.value[0]
|
||||
const pt2 = b2.value[0]
|
19
src/wasm-lib/tests/executor/inputs/negative_args.kcl
Normal file
19
src/wasm-lib/tests/executor/inputs/negative_args.kcl
Normal file
@ -0,0 +1,19 @@
|
||||
const width = 5
|
||||
const height = 10
|
||||
const length = 12
|
||||
|
||||
fn box = (sk1, sk2, scale) => {
|
||||
const boxSketch = startSketchOn('XY')
|
||||
|> startProfileAt([sk1, sk2], %)
|
||||
|> line([0, scale], %)
|
||||
|> line([scale, 0], %)
|
||||
|> line([0, -scale], %)
|
||||
|> close(%)
|
||||
|> extrude(scale, %)
|
||||
return boxSketch
|
||||
}
|
||||
|
||||
box(0, 0, 5)
|
||||
box(10, 23, 8)
|
||||
let thing = box(-12, -15, 10)
|
||||
box(-20, -5, 10)
|
18
src/wasm-lib/tests/executor/inputs/parametric.kcl
Normal file
18
src/wasm-lib/tests/executor/inputs/parametric.kcl
Normal file
@ -0,0 +1,18 @@
|
||||
const sigmaAllow = 35000 // psi
|
||||
const width = 9 // inch
|
||||
const p = 150 // Force on shelf - lbs
|
||||
const distance = 6 // inches
|
||||
const FOS = 2
|
||||
|
||||
const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
const thickness = sqrt(distance * p * FOS * 6 / sigmaAllow / width) // inches
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
|> line([-leg2 + thickness, 0], %)
|
||||
|> line([0, -leg1 + thickness], %)
|
||||
|> close(%)
|
||||
|> extrude(width, %)
|
@ -0,0 +1,26 @@
|
||||
const sigmaAllow = 15000 // psi
|
||||
const width = 11 // inch
|
||||
const p = 150 // Force on shelf - lbs
|
||||
const distance = 12 // inches
|
||||
const FOS = 2
|
||||
const thickness = sqrt(distance * p * FOS * 6 / ( sigmaAllow * width ))
|
||||
const filletR = thickness * 2
|
||||
const shelfMountL = 9
|
||||
const wallMountL = 8
|
||||
|
||||
const bracket = startSketchAt([0, 0])
|
||||
|> line([0, wallMountL], %)
|
||||
|> tangentialArc({
|
||||
radius: filletR,
|
||||
offset: 90
|
||||
}, %)
|
||||
|> line([-shelfMountL, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
|> line([shelfMountL, 0], %)
|
||||
|> tangentialArc({
|
||||
radius: filletR - thickness,
|
||||
offset: -90
|
||||
}, %)
|
||||
|> line([0, -wallMountL], %)
|
||||
|> close(%)
|
||||
|> extrude(width, %)
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user