Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
f97bdaf8b7 | |||
3f3693e12d | |||
73660d1db8 | |||
c373f33507 | |||
2dc76a71cc | |||
ce42966f2b | |||
b47b9c9613 | |||
2af2144f89 | |||
bd37c488ee | |||
a3551e4b2f | |||
d3979edb41 | |||
095a7a575b |
@ -2362,7 +2362,7 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
|
|||||||
|
|
||||||
await test.step('add new profile', async () => {
|
await test.step('add new profile', async () => {
|
||||||
await toolbar.rectangleBtn.click()
|
await toolbar.rectangleBtn.click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(200)
|
||||||
await rectStart()
|
await rectStart()
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
`profile005 = startProfile(sketch001, at = [15.68, -3.84])`
|
`profile005 = startProfile(sketch001, at = [15.68, -3.84])`
|
||||||
|
@ -3,102 +3,189 @@ import { uuidv4 } from '@src/lib/utils'
|
|||||||
|
|
||||||
import { getUtils } from '@e2e/playwright/test-utils'
|
import { getUtils } from '@e2e/playwright/test-utils'
|
||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
import type { Page } from '@playwright/test'
|
||||||
|
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||||
|
|
||||||
test.describe('Testing Camera Movement', () => {
|
test.describe('Testing Camera Movement', () => {
|
||||||
test('Can move camera reliably', async ({
|
/**
|
||||||
|
* hack that we're implemented our own retry instead of using retries built into playwright.
|
||||||
|
* however each of these camera drags can be flaky, because of udp
|
||||||
|
* and so putting them together means only one needs to fail to make this test extra flaky.
|
||||||
|
* this way we can retry within the test
|
||||||
|
* We could break them out into separate tests, but the longest past of the test is waiting
|
||||||
|
* for the stream to start, so it can be good to bundle related things together.
|
||||||
|
*/
|
||||||
|
const bakeInRetries = async ({
|
||||||
|
mouseActions,
|
||||||
|
afterPosition,
|
||||||
|
beforePosition,
|
||||||
|
retryCount = 0,
|
||||||
page,
|
page,
|
||||||
context,
|
|
||||||
homePage,
|
|
||||||
scene,
|
scene,
|
||||||
|
}: {
|
||||||
|
mouseActions: () => Promise<void>
|
||||||
|
beforePosition: [number, number, number]
|
||||||
|
afterPosition: [number, number, number]
|
||||||
|
retryCount?: number
|
||||||
|
page: Page
|
||||||
|
scene: SceneFixture
|
||||||
}) => {
|
}) => {
|
||||||
|
const acceptableCamError = 5
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await test.step('Set up initial camera position', async () =>
|
||||||
await scene.connectionEstablished()
|
await scene.moveCameraTo({
|
||||||
|
x: beforePosition[0],
|
||||||
|
y: beforePosition[1],
|
||||||
|
z: beforePosition[2],
|
||||||
|
}))
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await test.step('Do actions and watch for changes', async () =>
|
||||||
await u.closeKclCodePanel()
|
u.doAndWaitForImageDiff(async () => {
|
||||||
|
|
||||||
const camPos: [number, number, number] = [0, 85, 85]
|
|
||||||
const bakeInRetries = async (
|
|
||||||
mouseActions: any,
|
|
||||||
xyz: [number, number, number],
|
|
||||||
cnt = 0
|
|
||||||
) => {
|
|
||||||
// hack that we're implemented our own retry instead of using retries built into playwright.
|
|
||||||
// however each of these camera drags can be flaky, because of udp
|
|
||||||
// and so putting them together means only one needs to fail to make this test extra flaky.
|
|
||||||
// this way we can retry within the test
|
|
||||||
// We could break them out into separate tests, but the longest past of the test is waiting
|
|
||||||
// for the stream to start, so it can be good to bundle related things together.
|
|
||||||
|
|
||||||
const camCommand: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_look_at',
|
|
||||||
center: { x: 0, y: 0, z: 0 },
|
|
||||||
vantage: { x: camPos[0], y: camPos[1], z: camPos[2] },
|
|
||||||
up: { x: 0, y: 0, z: 1 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const updateCamCommand: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_get_settings',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await u.sendCustomCmd(camCommand)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await u.sendCustomCmd(updateCamCommand)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
// rotate
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
// const yo = page.getByTestId('cam-x-position').inputValue()
|
|
||||||
|
|
||||||
await u.doAndWaitForImageDiff(async () => {
|
|
||||||
await mouseActions()
|
await mouseActions()
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
}, 300)
|
}, 300))
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await page.getByTestId('cam-x-position').isVisible()
|
await expect(page.getByTestId('cam-x-position')).toBeAttached()
|
||||||
|
|
||||||
const vals = await Promise.all([
|
const vals = await Promise.all([
|
||||||
page.getByTestId('cam-x-position').inputValue(),
|
page.getByTestId('cam-x-position').inputValue(),
|
||||||
page.getByTestId('cam-y-position').inputValue(),
|
page.getByTestId('cam-y-position').inputValue(),
|
||||||
page.getByTestId('cam-z-position').inputValue(),
|
page.getByTestId('cam-z-position').inputValue(),
|
||||||
])
|
])
|
||||||
const xError = Math.abs(Number(vals[0]) + xyz[0])
|
const errors = vals.map((v, i) => Math.abs(Number(v) - afterPosition[i]))
|
||||||
const yError = Math.abs(Number(vals[1]) + xyz[1])
|
|
||||||
const zError = Math.abs(Number(vals[2]) + xyz[2])
|
|
||||||
|
|
||||||
let shouldRetry = false
|
let shouldRetry = false
|
||||||
|
|
||||||
if (xError > 5 || yError > 5 || zError > 5) {
|
if (errors.some((e) => e > acceptableCamError)) {
|
||||||
if (cnt > 2) {
|
if (retryCount > 2) {
|
||||||
console.log('xVal', vals[0], 'xError', xError)
|
console.log('xVal', vals[0], 'xError', errors[0])
|
||||||
console.log('yVal', vals[1], 'yError', yError)
|
console.log('yVal', vals[1], 'yError', errors[1])
|
||||||
console.log('zVal', vals[2], 'zError', zError)
|
console.log('zVal', vals[2], 'zError', errors[2])
|
||||||
|
|
||||||
throw new Error('Camera position not as expected')
|
throw new Error('Camera position not as expected', {
|
||||||
|
cause: {
|
||||||
|
vals,
|
||||||
|
errors,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
shouldRetry = true
|
shouldRetry = true
|
||||||
}
|
}
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
if (shouldRetry) {
|
||||||
await page.waitForTimeout(100)
|
await bakeInRetries({
|
||||||
if (shouldRetry) await bakeInRetries(mouseActions, xyz, cnt + 1)
|
mouseActions,
|
||||||
|
afterPosition: afterPosition,
|
||||||
|
beforePosition: beforePosition,
|
||||||
|
retryCount: retryCount + 1,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
await bakeInRetries(async () => {
|
}
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Can pan and zoom camera reliably',
|
||||||
|
{
|
||||||
|
tag: '@web',
|
||||||
|
},
|
||||||
|
async ({ page, homePage, scene, cmdBar }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
const camInitialPosition: [number, number, number] = [0, 85, 85]
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await u.closeKclCodePanel()
|
||||||
|
|
||||||
|
await test.step('Pan', async () => {
|
||||||
|
await bakeInRetries({
|
||||||
|
mouseActions: async () => {
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.mouse.move(600, 200)
|
||||||
|
await page.mouse.down({ button: 'right' })
|
||||||
|
// Gotcha: remove steps:2 from this 700,200 mouse move. This bricked the test on local host engine.
|
||||||
|
await page.mouse.move(700, 200)
|
||||||
|
await page.mouse.up({ button: 'right' })
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
},
|
||||||
|
afterPosition: [19, 85, 85],
|
||||||
|
beforePosition: camInitialPosition,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Zoom with click and drag', async () => {
|
||||||
|
await bakeInRetries({
|
||||||
|
mouseActions: async () => {
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.mouse.move(700, 400)
|
||||||
|
await page.mouse.down({ button: 'right' })
|
||||||
|
await page.mouse.move(700, 300)
|
||||||
|
await page.mouse.up({ button: 'right' })
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
},
|
||||||
|
afterPosition: [0, 118, 118],
|
||||||
|
beforePosition: camInitialPosition,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Zoom with scrollwheel', async () => {
|
||||||
|
const refreshCamValuesCmd: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await bakeInRetries({
|
||||||
|
mouseActions: async () => {
|
||||||
|
await page.mouse.move(700, 400)
|
||||||
|
await page.mouse.wheel(0, -150)
|
||||||
|
|
||||||
|
// Scroll zooming doesn't update the debug pane's cam position values,
|
||||||
|
// so we have to force a refresh.
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await u.sendCustomCmd(refreshCamValuesCmd)
|
||||||
|
await u.waitForCmdReceive('default_camera_get_settings')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
},
|
||||||
|
afterPosition: [0, 42.5, 42.5],
|
||||||
|
beforePosition: camInitialPosition,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Can orbit camera reliably',
|
||||||
|
{
|
||||||
|
tag: '@web',
|
||||||
|
},
|
||||||
|
async ({ page, homePage, scene, cmdBar }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
const initialCamPosition: [number, number, number] = [0, 85, 85]
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
// this turns on the debug pane setting as well
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await u.closeKclCodePanel()
|
||||||
|
|
||||||
|
await test.step('Test orbit with spherical mode', async () => {
|
||||||
|
await bakeInRetries({
|
||||||
|
mouseActions: async () => {
|
||||||
await page.mouse.move(700, 200)
|
await page.mouse.move(700, 200)
|
||||||
await page.mouse.down({ button: 'right' })
|
await page.mouse.down({ button: 'right' })
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
@ -114,76 +201,52 @@ test.describe('Testing Camera Movement', () => {
|
|||||||
await page.mouse.move(600, 303)
|
await page.mouse.move(600, 303)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.up({ button: 'right' })
|
await page.mouse.up({ button: 'right' })
|
||||||
}, [4, -10.5, -120])
|
|
||||||
|
|
||||||
await bakeInRetries(async () => {
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.mouse.move(600, 200)
|
|
||||||
await page.mouse.down({ button: 'right' })
|
|
||||||
// Gotcha: remove steps:2 from this 700,200 mouse move. This bricked the test on local host engine.
|
|
||||||
await page.mouse.move(700, 200)
|
|
||||||
await page.mouse.up({ button: 'right' })
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
}, [-19, -85, -85])
|
|
||||||
|
|
||||||
const camCommand: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_look_at',
|
|
||||||
center: { x: 0, y: 0, z: 0 },
|
|
||||||
vantage: { x: camPos[0], y: camPos[1], z: camPos[2] },
|
|
||||||
up: { x: 0, y: 0, z: 1 },
|
|
||||||
},
|
},
|
||||||
}
|
afterPosition: [-4, 10.5, 120],
|
||||||
const updateCamCommand: EngineCommand = {
|
beforePosition: initialCamPosition,
|
||||||
type: 'modeling_cmd_req',
|
page,
|
||||||
cmd_id: uuidv4(),
|
scene,
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_get_settings',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await u.sendCustomCmd(camCommand)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await u.sendCustomCmd(updateCamCommand)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await u.clearCommandLogs()
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
|
|
||||||
// zoom
|
|
||||||
await u.doAndWaitForImageDiff(async () => {
|
|
||||||
await page.keyboard.down('Control')
|
|
||||||
await page.mouse.move(700, 400)
|
|
||||||
await page.mouse.down({ button: 'right' })
|
|
||||||
await page.mouse.move(700, 300)
|
|
||||||
await page.mouse.up({ button: 'right' })
|
|
||||||
await page.keyboard.up('Control')
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await page.waitForTimeout(300)
|
|
||||||
await u.clearCommandLogs()
|
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
}, 300)
|
|
||||||
|
|
||||||
// zoom with scroll
|
|
||||||
await u.openAndClearDebugPanel()
|
|
||||||
// TODO, it appears we don't get the cam setting back from the engine when the interaction is zoom into `backInRetries` once the information is sent back on zoom
|
|
||||||
// await expect(Math.abs(Number(await page.getByTestId('cam-x-position').inputValue()) + 12)).toBeLessThan(1.5)
|
|
||||||
// await expect(Math.abs(Number(await page.getByTestId('cam-y-position').inputValue()) - 85)).toBeLessThan(1.5)
|
|
||||||
// await expect(Math.abs(Number(await page.getByTestId('cam-z-position').inputValue()) - 85)).toBeLessThan(1.5)
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
||||||
|
|
||||||
await bakeInRetries(async () => {
|
|
||||||
await page.mouse.move(700, 400)
|
|
||||||
await page.mouse.wheel(0, -100)
|
|
||||||
}, [0, -85, -85])
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Test orbit with trackball mode', async () => {
|
||||||
|
await test.step('Set orbitMode to trackball', async () => {
|
||||||
|
await cmdBar.openCmdBar()
|
||||||
|
await cmdBar.selectOption({ name: 'camera orbit' }).click()
|
||||||
|
await cmdBar.selectOption({ name: 'trackball' }).click()
|
||||||
|
await expect(
|
||||||
|
page.getByText(`camera orbit to "trackball"`)
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await bakeInRetries({
|
||||||
|
mouseActions: async () => {
|
||||||
|
await page.mouse.move(700, 200)
|
||||||
|
await page.mouse.down({ button: 'right' })
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
const appLogoBBox = await page.getByTestId('app-logo').boundingBox()
|
||||||
|
expect(appLogoBBox).not.toBeNull()
|
||||||
|
if (!appLogoBBox) {
|
||||||
|
throw new Error('app logo not found')
|
||||||
|
}
|
||||||
|
await page.mouse.move(
|
||||||
|
appLogoBBox.x + appLogoBBox.width / 2,
|
||||||
|
appLogoBBox.y + appLogoBBox.height / 2
|
||||||
|
)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.mouse.move(600, 303)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.mouse.up({ button: 'right' })
|
||||||
|
},
|
||||||
|
afterPosition: [18.06, -42.79, 110.87],
|
||||||
|
beforePosition: initialCamPosition,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// TODO: fix after electron migration is merged
|
// TODO: fix after electron migration is merged
|
||||||
test('Zoom should be consistent when exiting or entering sketches', async ({
|
test('Zoom should be consistent when exiting or entering sketches', async ({
|
||||||
|
47
rust/Cargo.lock
generated
47
rust/Cargo.lock
generated
@ -627,26 +627,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "criterion"
|
name = "criterion"
|
||||||
version = "0.5.1"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anes",
|
"anes",
|
||||||
"cast",
|
"cast",
|
||||||
"ciborium",
|
"ciborium",
|
||||||
"clap",
|
"clap",
|
||||||
"criterion-plot",
|
"criterion-plot",
|
||||||
"futures",
|
"itertools 0.13.0",
|
||||||
"is-terminal",
|
|
||||||
"itertools 0.10.5",
|
|
||||||
"num-traits 0.2.19",
|
"num-traits 0.2.19",
|
||||||
"once_cell",
|
|
||||||
"oorandom",
|
"oorandom",
|
||||||
"plotters",
|
"plotters",
|
||||||
"rayon",
|
"rayon",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tinytemplate",
|
"tinytemplate",
|
||||||
"tokio",
|
"tokio",
|
||||||
@ -715,9 +711,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "csscolorparser"
|
name = "csscolorparser"
|
||||||
version = "0.7.0"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46f9a16a848a7fb95dd47ce387ac1ee9a6df879ba784b815537fcd388a1a8288"
|
checksum = "5fda6aace1fbef3aa217b27f4c8d7d071ef2a70a5ca51050b1f17d40299d3f16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf",
|
"phf",
|
||||||
]
|
]
|
||||||
@ -1815,7 +1811,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1826,7 +1822,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -1845,7 +1841,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -1855,7 +1851,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
version = "0.2.78"
|
version = "0.2.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1876,7 +1872,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1896,7 +1892,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.78"
|
version = "0.2.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
@ -1973,7 +1969,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-python-bindings"
|
name = "kcl-python-bindings"
|
||||||
version = "0.3.78"
|
version = "0.3.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
@ -1988,7 +1984,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"hyper 0.14.32",
|
"hyper 0.14.32",
|
||||||
@ -2001,7 +1997,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-to-core"
|
name = "kcl-to-core"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2015,7 +2011,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-wasm-lib"
|
name = "kcl-wasm-lib"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bson",
|
"bson",
|
||||||
@ -4200,9 +4196,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-tungstenite"
|
name = "tokio-tungstenite"
|
||||||
version = "0.24.0"
|
version = "0.26.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
|
checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
@ -4456,21 +4452,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.24.0"
|
version = "0.26.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.5",
|
"rand 0.9.0",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.12",
|
||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/KittyCAD/modeling-api"
|
repository = "https://github.com/KittyCAD/modeling-api"
|
||||||
rust-version = "1.76"
|
rust-version = "1.76"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
description = "A tool for generating documentation from Rust derive macros"
|
description = "A tool for generating documentation from Rust derive macros"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
description = "A tool for generating tests from a directory of kcl files"
|
description = "A tool for generating tests from a directory of kcl files"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
publish = false
|
publish = false
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
description = "A language server for KCL."
|
description = "A language server for KCL."
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
version = "0.2.78"
|
version = "0.2.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language implementation and tools"
|
description = "KittyCAD Language implementation and tools"
|
||||||
version = "0.2.78"
|
version = "0.2.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
@ -31,7 +31,7 @@ clap = { version = "4.5.36", default-features = false, optional = true, features
|
|||||||
"derive",
|
"derive",
|
||||||
] }
|
] }
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.8.0"
|
||||||
csscolorparser = "0.7.0"
|
csscolorparser = "0.7.2"
|
||||||
dashmap = { workspace = true }
|
dashmap = { workspace = true }
|
||||||
dhat = { version = "0.3", optional = true }
|
dhat = { version = "0.3", optional = true }
|
||||||
fnv = "1.0.7"
|
fnv = "1.0.7"
|
||||||
@ -107,7 +107,7 @@ web-sys = { version = "0.3.76", features = ["console"] }
|
|||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
instant = "0.1.13"
|
instant = "0.1.13"
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
tokio-tungstenite = { version = "0.24.0", features = [
|
tokio-tungstenite = { version = "0.26.2", features = [
|
||||||
"rustls-tls-native-roots",
|
"rustls-tls-native-roots",
|
||||||
] }
|
] }
|
||||||
tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
||||||
@ -130,7 +130,7 @@ tabled = ["dep:tabled"]
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
approx = "0.5"
|
approx = "0.5"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
criterion = { version = "0.5.1", features = ["async_tokio"] }
|
criterion = { version = "0.6.0", features = ["async_tokio"] }
|
||||||
expectorate = "1.1.0"
|
expectorate = "1.1.0"
|
||||||
handlebars = "6.3.2"
|
handlebars = "6.3.2"
|
||||||
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
|
hint::black_box,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
const IGNORE_DIRS: [&str; 2] = ["step", "screenshots"];
|
const IGNORE_DIRS: [&str; 2] = ["step", "screenshots"];
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use std::hint::black_box;
|
||||||
|
|
||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
pub fn bench_parse(c: &mut Criterion) {
|
pub fn bench_parse(c: &mut Criterion) {
|
||||||
for (name, file) in [
|
for (name, file) in [
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
|
use std::hint::black_box;
|
||||||
|
|
||||||
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
use kcl_lib::kcl_lsp_server;
|
use kcl_lib::kcl_lsp_server;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tower_lsp::LanguageServer;
|
use tower_lsp::LanguageServer;
|
||||||
|
@ -67,6 +67,7 @@ pub struct TcpRead {
|
|||||||
|
|
||||||
/// Occurs when client couldn't read from the WebSocket to the engine.
|
/// Occurs when client couldn't read from the WebSocket to the engine.
|
||||||
// #[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum WebSocketReadError {
|
pub enum WebSocketReadError {
|
||||||
/// Could not read a message due to WebSocket errors.
|
/// Could not read a message due to WebSocket errors.
|
||||||
Read(tokio_tungstenite::tungstenite::Error),
|
Read(tokio_tungstenite::tungstenite::Error),
|
||||||
@ -206,7 +207,7 @@ impl EngineConnection {
|
|||||||
async fn inner_send_to_engine(request: WebSocketRequest, tcp_write: &mut WebSocketTcpWrite) -> Result<()> {
|
async fn inner_send_to_engine(request: WebSocketRequest, tcp_write: &mut WebSocketTcpWrite) -> Result<()> {
|
||||||
let msg = serde_json::to_string(&request).map_err(|e| anyhow!("could not serialize json: {e}"))?;
|
let msg = serde_json::to_string(&request).map_err(|e| anyhow!("could not serialize json: {e}"))?;
|
||||||
tcp_write
|
tcp_write
|
||||||
.send(WsMsg::Text(msg))
|
.send(WsMsg::Text(msg.into()))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!("could not send json over websocket: {e}"))?;
|
.map_err(|e| anyhow!("could not send json over websocket: {e}"))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -216,19 +217,17 @@ impl EngineConnection {
|
|||||||
async fn inner_send_to_engine_binary(request: WebSocketRequest, tcp_write: &mut WebSocketTcpWrite) -> Result<()> {
|
async fn inner_send_to_engine_binary(request: WebSocketRequest, tcp_write: &mut WebSocketTcpWrite) -> Result<()> {
|
||||||
let msg = bson::to_vec(&request).map_err(|e| anyhow!("could not serialize bson: {e}"))?;
|
let msg = bson::to_vec(&request).map_err(|e| anyhow!("could not serialize bson: {e}"))?;
|
||||||
tcp_write
|
tcp_write
|
||||||
.send(WsMsg::Binary(msg))
|
.send(WsMsg::Binary(msg.into()))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!("could not send json over websocket: {e}"))?;
|
.map_err(|e| anyhow!("could not send json over websocket: {e}"))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new(ws: reqwest::Upgraded) -> Result<EngineConnection> {
|
pub async fn new(ws: reqwest::Upgraded) -> Result<EngineConnection> {
|
||||||
let wsconfig = tokio_tungstenite::tungstenite::protocol::WebSocketConfig {
|
let wsconfig = tokio_tungstenite::tungstenite::protocol::WebSocketConfig::default()
|
||||||
// 4294967296 bytes, which is around 4.2 GB.
|
// 4294967296 bytes, which is around 4.2 GB.
|
||||||
max_message_size: Some(usize::MAX),
|
.max_message_size(Some(usize::MAX))
|
||||||
max_frame_size: Some(usize::MAX),
|
.max_frame_size(Some(usize::MAX));
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let ws_stream = tokio_tungstenite::WebSocketStream::from_raw_socket(
|
let ws_stream = tokio_tungstenite::WebSocketStream::from_raw_socket(
|
||||||
ws,
|
ws,
|
||||||
|
@ -304,9 +304,10 @@ impl ExecutorContext {
|
|||||||
|
|
||||||
let annotations = &variable_declaration.outer_attrs;
|
let annotations = &variable_declaration.outer_attrs;
|
||||||
|
|
||||||
// During the evaluation of the variable's LHS, set context that this is all happening inside a variable
|
// During the evaluation of the variable's RHS, set context that this is all happening inside a variable
|
||||||
// declaration, for the given name. This helps improve user-facing error messages.
|
// declaration, for the given name. This helps improve user-facing error messages.
|
||||||
exec_state.mod_local.being_declared = Some(variable_declaration.inner.name().to_owned());
|
let lhs = variable_declaration.inner.name().to_owned();
|
||||||
|
exec_state.mod_local.being_declared = Some(lhs);
|
||||||
let rhs_result = self
|
let rhs_result = self
|
||||||
.execute_expr(
|
.execute_expr(
|
||||||
&variable_declaration.declaration.init,
|
&variable_declaration.declaration.init,
|
||||||
@ -645,7 +646,12 @@ impl ExecutorContext {
|
|||||||
Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), exec_state),
|
Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), exec_state),
|
||||||
Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
|
Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
|
||||||
Expr::Name(name) => {
|
Expr::Name(name) => {
|
||||||
let value = name.get_result(exec_state, self).await?.clone();
|
let being_declared = exec_state.mod_local.being_declared.clone();
|
||||||
|
let value = name
|
||||||
|
.get_result(exec_state, self)
|
||||||
|
.await
|
||||||
|
.map_err(|e| var_in_own_ref_err(e, &being_declared))?
|
||||||
|
.clone();
|
||||||
if let KclValue::Module { value: module_id, meta } = value {
|
if let KclValue::Module { value: module_id, meta } = value {
|
||||||
self.exec_module_for_result(
|
self.exec_module_for_result(
|
||||||
module_id,
|
module_id,
|
||||||
@ -751,6 +757,24 @@ impl ExecutorContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the error is about an undefined name, and that name matches the name being defined,
|
||||||
|
/// make the error message more specific.
|
||||||
|
fn var_in_own_ref_err(e: KclError, being_declared: &Option<String>) -> KclError {
|
||||||
|
let KclError::UndefinedValue { name, mut details } = e else {
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
// TODO after June 26th: replace this with a let-chain,
|
||||||
|
// which will be available in Rust 1.88
|
||||||
|
// https://rust-lang.github.io/rfcs/2497-if-let-chains.html
|
||||||
|
match (&being_declared, &name) {
|
||||||
|
(Some(name0), Some(name1)) if name0 == name1 => {
|
||||||
|
details.message = format!("You can't use `{name0}` because you're currently trying to define it. Use a different variable here instead.");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
KclError::UndefinedValue { details, name }
|
||||||
|
}
|
||||||
|
|
||||||
impl Node<AscribedExpression> {
|
impl Node<AscribedExpression> {
|
||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||||
@ -1327,7 +1351,7 @@ pub(crate) async fn execute_pipe_body(
|
|||||||
// Now that we've evaluated the first child expression in the pipeline, following child expressions
|
// Now that we've evaluated the first child expression in the pipeline, following child expressions
|
||||||
// should use the previous child expression for %.
|
// should use the previous child expression for %.
|
||||||
// This means there's no more need for the previous pipe_value from the parent AST node above this one.
|
// This means there's no more need for the previous pipe_value from the parent AST node above this one.
|
||||||
let previous_pipe_value = std::mem::replace(&mut exec_state.mod_local.pipe_value, Some(output));
|
let previous_pipe_value = exec_state.mod_local.pipe_value.replace(output);
|
||||||
// Evaluate remaining elements.
|
// Evaluate remaining elements.
|
||||||
let result = inner_execute_pipe_body(exec_state, body, ctx).await;
|
let result = inner_execute_pipe_body(exec_state, body, ctx).await;
|
||||||
// Restore the previous pipe value.
|
// Restore the previous pipe value.
|
||||||
|
@ -24,6 +24,7 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
|||||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum Geometry {
|
pub enum Geometry {
|
||||||
Sketch(Sketch),
|
Sketch(Sketch),
|
||||||
Solid(Solid),
|
Solid(Solid),
|
||||||
@ -52,6 +53,7 @@ impl Geometry {
|
|||||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum GeometryWithImportedGeometry {
|
pub enum GeometryWithImportedGeometry {
|
||||||
Sketch(Sketch),
|
Sketch(Sketch),
|
||||||
Solid(Solid),
|
Solid(Solid),
|
||||||
|
@ -187,7 +187,7 @@ impl RuntimeType {
|
|||||||
};
|
};
|
||||||
RuntimeType::Primitive(PrimitiveType::Number(ty))
|
RuntimeType::Primitive(PrimitiveType::Number(ty))
|
||||||
}
|
}
|
||||||
AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
|
AstPrimitiveType::Named { id } => Self::from_alias(&id.name, exec_state, source_range)?,
|
||||||
AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
|
AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
|
||||||
AstPrimitiveType::ImportedGeometry => RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
AstPrimitiveType::ImportedGeometry => RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||||
AstPrimitiveType::Function(_) => RuntimeType::Primitive(PrimitiveType::Function),
|
AstPrimitiveType::Function(_) => RuntimeType::Primitive(PrimitiveType::Function),
|
||||||
|
@ -1189,7 +1189,6 @@ impl LanguageServer for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn completion(&self, params: CompletionParams) -> RpcResult<Option<CompletionResponse>> {
|
async fn completion(&self, params: CompletionParams) -> RpcResult<Option<CompletionResponse>> {
|
||||||
// ADAM: This is the entrypoint.
|
|
||||||
let mut completions = vec![CompletionItem {
|
let mut completions = vec![CompletionItem {
|
||||||
label: PIPE_OPERATOR.to_string(),
|
label: PIPE_OPERATOR.to_string(),
|
||||||
label_details: None,
|
label_details: None,
|
||||||
|
@ -228,7 +228,7 @@ impl PrimitiveType {
|
|||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
match self {
|
match self {
|
||||||
PrimitiveType::Any => hasher.update(b"any"),
|
PrimitiveType::Any => hasher.update(b"any"),
|
||||||
PrimitiveType::Named(id) => hasher.update(id.compute_digest()),
|
PrimitiveType::Named { id } => hasher.update(id.compute_digest()),
|
||||||
PrimitiveType::String => hasher.update(b"string"),
|
PrimitiveType::String => hasher.update(b"string"),
|
||||||
PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),
|
PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),
|
||||||
PrimitiveType::Boolean => hasher.update(b"bool"),
|
PrimitiveType::Boolean => hasher.update(b"bool"),
|
||||||
|
@ -454,7 +454,7 @@ impl Node<Program> {
|
|||||||
alpha: c.a,
|
alpha: c.a,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if colors.borrow().iter().any(|c| *c == color) {
|
if colors.borrow().contains(&color) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
colors.borrow_mut().push(color);
|
colors.borrow_mut().push(color);
|
||||||
@ -529,7 +529,7 @@ impl Node<Program> {
|
|||||||
let new_color = csscolorparser::Color::new(color.red, color.green, color.blue, color.alpha);
|
let new_color = csscolorparser::Color::new(color.red, color.green, color.blue, color.alpha);
|
||||||
Ok(Some(ColorPresentation {
|
Ok(Some(ColorPresentation {
|
||||||
// The label will be what they replace the color with.
|
// The label will be what they replace the color with.
|
||||||
label: new_color.to_hex_string(),
|
label: new_color.to_css_hex(),
|
||||||
text_edit: None,
|
text_edit: None,
|
||||||
additional_text_edits: None,
|
additional_text_edits: None,
|
||||||
}))
|
}))
|
||||||
@ -3230,7 +3230,7 @@ pub enum PrimitiveType {
|
|||||||
/// `fn`, type of functions.
|
/// `fn`, type of functions.
|
||||||
Function(FunctionType),
|
Function(FunctionType),
|
||||||
/// An identifier used as a type (not really a primitive type, but whatever).
|
/// An identifier used as a type (not really a primitive type, but whatever).
|
||||||
Named(Node<Identifier>),
|
Named { id: Node<Identifier> },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrimitiveType {
|
impl PrimitiveType {
|
||||||
@ -3286,7 +3286,7 @@ impl fmt::Display for PrimitiveType {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
PrimitiveType::Named(n) => write!(f, "{}", n.name),
|
PrimitiveType::Named { id: n } => write!(f, "{}", n.name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2938,7 +2938,7 @@ fn primitive_type(i: &mut TokenSlice) -> ModalResult<Node<PrimitiveType>> {
|
|||||||
(identifier, opt(delimited(open_paren, uom_for_type, close_paren))).map(|(ident, suffix)| {
|
(identifier, opt(delimited(open_paren, uom_for_type, close_paren))).map(|(ident, suffix)| {
|
||||||
let mut result = Node::new(PrimitiveType::Boolean, ident.start, ident.end, ident.module_id);
|
let mut result = Node::new(PrimitiveType::Boolean, ident.start, ident.end, ident.module_id);
|
||||||
result.inner =
|
result.inner =
|
||||||
PrimitiveType::primitive_from_str(&ident.name, suffix).unwrap_or(PrimitiveType::Named(ident));
|
PrimitiveType::primitive_from_str(&ident.name, suffix).unwrap_or(PrimitiveType::Named { id: ident });
|
||||||
result
|
result
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
|
@ -3504,3 +3504,24 @@ mod var_ref_in_own_def {
|
|||||||
super::execute(TEST_NAME, true).await
|
super::execute(TEST_NAME, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mod ascription_unknown_type {
|
||||||
|
const TEST_NAME: &str = "ascription_unknown_type";
|
||||||
|
|
||||||
|
/// Test parsing KCL.
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
super::parse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn unparse() {
|
||||||
|
super::unparse(TEST_NAME).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that KCL is executed correctly.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_execute() {
|
||||||
|
super::execute(TEST_NAME, true).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@ use crate::{
|
|||||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum SweepPath {
|
pub enum SweepPath {
|
||||||
Sketch(Sketch),
|
Sketch(Sketch),
|
||||||
Helix(Box<Helix>),
|
Helix(Box<Helix>),
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Artifact commands ascription_unknown_type.kcl
|
||||||
|
---
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [],
|
||||||
|
"command": {
|
||||||
|
"type": "edge_lines_visible",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [],
|
||||||
|
"command": {
|
||||||
|
"type": "object_visible",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [],
|
||||||
|
"command": {
|
||||||
|
"type": "object_visible",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart ascription_unknown_type.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
67
rust/kcl-lib/tests/ascription_unknown_type/ast.snap
Normal file
67
rust/kcl-lib/tests/ascription_unknown_type/ast.snap
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Result of parsing ascription_unknown_type.kcl
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"Ok": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"commentStart": 0,
|
||||||
|
"declaration": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"id": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"name": "z",
|
||||||
|
"start": 0,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"expr": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"raw": "10",
|
||||||
|
"start": 0,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 10.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 0,
|
||||||
|
"ty": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"id": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"name": "NotARealType",
|
||||||
|
"start": 0,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"p_type": "Named",
|
||||||
|
"start": 0,
|
||||||
|
"type": "Primitive"
|
||||||
|
},
|
||||||
|
"type": "AscribedExpression",
|
||||||
|
"type": "AscribedExpression"
|
||||||
|
},
|
||||||
|
"start": 0,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 0,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 0,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"start": 0
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Error from executing ascription_unknown_type.kcl
|
||||||
|
---
|
||||||
|
KCL Semantic error
|
||||||
|
|
||||||
|
× semantic: Unknown type: NotARealType
|
||||||
|
╭────
|
||||||
|
1 │ z = 10: NotARealType
|
||||||
|
· ─┬
|
||||||
|
· ╰── tests/ascription_unknown_type/input.kcl
|
||||||
|
╰────
|
1
rust/kcl-lib/tests/ascription_unknown_type/input.kcl
Normal file
1
rust/kcl-lib/tests/ascription_unknown_type/input.kcl
Normal file
@ -0,0 +1 @@
|
|||||||
|
z = 10: NotARealType
|
5
rust/kcl-lib/tests/ascription_unknown_type/ops.snap
Normal file
5
rust/kcl-lib/tests/ascription_unknown_type/ops.snap
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Operations executed ascription_unknown_type.kcl
|
||||||
|
---
|
||||||
|
[]
|
5
rust/kcl-lib/tests/ascription_unknown_type/unparsed.snap
Normal file
5
rust/kcl-lib/tests/ascription_unknown_type/unparsed.snap
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Result of unparsing ascription_unknown_type.kcl
|
||||||
|
---
|
||||||
|
z = 10: NotARealType
|
@ -28,5 +28,30 @@ description: Artifact commands var_ref_in_own_def.kcl
|
|||||||
"object_id": "[uuid]",
|
"object_id": "[uuid]",
|
||||||
"hidden": true
|
"hidden": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 60.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
|
1["Plane<br>[95, 112, 0]"]
|
||||||
|
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
|
||||||
```
|
```
|
||||||
|
@ -13,11 +13,13 @@ description: Result of parsing var_ref_in_own_def.kcl
|
|||||||
"id": {
|
"id": {
|
||||||
"commentStart": 0,
|
"commentStart": 0,
|
||||||
"end": 0,
|
"end": 0,
|
||||||
"name": "x",
|
"name": "sketch001",
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"type": "Identifier"
|
"type": "Identifier"
|
||||||
},
|
},
|
||||||
"init": {
|
"init": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
"callee": {
|
"callee": {
|
||||||
"abs_path": false,
|
"abs_path": false,
|
||||||
"commentStart": 0,
|
"commentStart": 0,
|
||||||
@ -25,7 +27,7 @@ description: Result of parsing var_ref_in_own_def.kcl
|
|||||||
"name": {
|
"name": {
|
||||||
"commentStart": 0,
|
"commentStart": 0,
|
||||||
"end": 0,
|
"end": 0,
|
||||||
"name": "cos",
|
"name": "startSketchOn",
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"type": "Identifier"
|
"type": "Identifier"
|
||||||
},
|
},
|
||||||
@ -45,7 +47,7 @@ description: Result of parsing var_ref_in_own_def.kcl
|
|||||||
"name": {
|
"name": {
|
||||||
"commentStart": 0,
|
"commentStart": 0,
|
||||||
"end": 0,
|
"end": 0,
|
||||||
"name": "x",
|
"name": "XY",
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"type": "Identifier"
|
"type": "Identifier"
|
||||||
},
|
},
|
||||||
@ -55,13 +57,105 @@ description: Result of parsing var_ref_in_own_def.kcl
|
|||||||
"type": "Name"
|
"type": "Name"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"type": "LabeledArg",
|
||||||
|
"label": null,
|
||||||
|
"arg": {
|
||||||
|
"abs_path": false,
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"name": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"name": "sketch001",
|
||||||
|
"start": 0,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"path": [],
|
||||||
|
"start": 0,
|
||||||
|
"type": "Name",
|
||||||
|
"type": "Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"abs_path": false,
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"name": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"name": "startProfileAt",
|
||||||
|
"start": 0,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"path": [],
|
||||||
|
"start": 0,
|
||||||
|
"type": "Name"
|
||||||
|
},
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"start": 0,
|
||||||
|
"type": "CallExpressionKw",
|
||||||
|
"type": "CallExpressionKw",
|
||||||
|
"unlabeled": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"raw": "20",
|
||||||
|
"start": 0,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 20.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"argument": {
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"raw": "20",
|
||||||
|
"start": 0,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 20.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 0,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 0,
|
||||||
|
"start": 0,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commentStart": 0,
|
||||||
|
"end": 0,
|
||||||
|
"start": 0,
|
||||||
|
"type": "PipeExpression",
|
||||||
|
"type": "PipeExpression"
|
||||||
|
},
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"type": "VariableDeclarator"
|
"type": "VariableDeclarator"
|
||||||
},
|
},
|
||||||
"end": 0,
|
"end": 0,
|
||||||
"kind": "const",
|
"kind": "const",
|
||||||
"preComments": [
|
"preComments": [
|
||||||
"// This won't work, because `x` is being referenced in its own definition."
|
"// This won't work, because `sketch001` is being referenced in its own definition."
|
||||||
],
|
],
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"type": "VariableDeclaration",
|
"type": "VariableDeclaration",
|
||||||
|
@ -4,10 +4,11 @@ description: Error from executing var_ref_in_own_def.kcl
|
|||||||
---
|
---
|
||||||
KCL UndefinedValue error
|
KCL UndefinedValue error
|
||||||
|
|
||||||
× undefined value: `x` is not defined
|
× undefined value: You can't use `sketch001` because you're currently trying
|
||||||
╭─[2:9]
|
│ to define it. Use a different variable here instead.
|
||||||
1 │ // This won't work, because `x` is being referenced in its own definition.
|
╭─[3:32]
|
||||||
2 │ x = cos(x)
|
2 │ sketch001 = startSketchOn(XY)
|
||||||
· ┬
|
3 │ |> startProfileAt([20, -20], sketch001)
|
||||||
|
· ────┬────
|
||||||
· ╰── tests/var_ref_in_own_def/input.kcl
|
· ╰── tests/var_ref_in_own_def/input.kcl
|
||||||
╰────
|
╰────
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
// This won't work, because `x` is being referenced in its own definition.
|
// This won't work, because `sketch001` is being referenced in its own definition.
|
||||||
x = cos(x)
|
sketch001 = startSketchOn(XY)
|
||||||
|
|> startProfileAt([20, -20], sketch001)
|
||||||
|
@ -2,4 +2,18 @@
|
|||||||
source: kcl-lib/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed var_ref_in_own_def.kcl
|
description: Operations executed var_ref_in_own_def.kcl
|
||||||
---
|
---
|
||||||
[]
|
[
|
||||||
|
{
|
||||||
|
"type": "StdLibCall",
|
||||||
|
"name": "startSketchOn",
|
||||||
|
"unlabeledArg": {
|
||||||
|
"value": {
|
||||||
|
"type": "Plane",
|
||||||
|
"artifact_id": "[uuid]"
|
||||||
|
},
|
||||||
|
"sourceRange": []
|
||||||
|
},
|
||||||
|
"labeledArgs": {},
|
||||||
|
"sourceRange": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
@ -2,5 +2,6 @@
|
|||||||
source: kcl-lib/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Result of unparsing var_ref_in_own_def.kcl
|
description: Result of unparsing var_ref_in_own_def.kcl
|
||||||
---
|
---
|
||||||
// This won't work, because `x` is being referenced in its own definition.
|
// This won't work, because `sketch001` is being referenced in its own definition.
|
||||||
x = cos(x)
|
sketch001 = startSketchOn(XY)
|
||||||
|
|> startProfileAt([20, -20], sketch001)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-python-bindings"
|
name = "kcl-python-bindings"
|
||||||
version = "0.3.78"
|
version = "0.3.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/kittycad/modeling-app"
|
repository = "https://github.com/kittycad/modeling-app"
|
||||||
exclude = ["tests/*", "files/*", "venv/*"]
|
exclude = ["tests/*", "files/*", "venv/*"]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
description = "A test server for KCL"
|
description = "A test server for KCL"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-to-core"
|
name = "kcl-to-core"
|
||||||
description = "Utility methods to convert kcl to engine core executable tests"
|
description = "Utility methods to convert kcl to engine core executable tests"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-wasm-lib"
|
name = "kcl-wasm-lib"
|
||||||
version = "0.1.78"
|
version = "0.1.79"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
rust-version = "1.83"
|
rust-version = "1.83"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use gloo_utils::format::JsValueSerdeExt;
|
use gloo_utils::format::JsValueSerdeExt;
|
||||||
use kcl_lib::{wasm_engine::FileManager, EngineManager, Program};
|
use kcl_lib::{wasm_engine::FileManager, EngineManager, ExecOutcome, KclError, KclErrorWithOutputs, Program};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@ -56,7 +56,7 @@ impl Context {
|
|||||||
return Ok(kcl_lib::ExecutorContext::new_mock(
|
return Ok(kcl_lib::ExecutorContext::new_mock(
|
||||||
self.mock_engine.clone(),
|
self.mock_engine.clone(),
|
||||||
self.fs.clone(),
|
self.fs.clone(),
|
||||||
settings.into(),
|
settings,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,18 +74,38 @@ impl Context {
|
|||||||
program_ast_json: &str,
|
program_ast_json: &str,
|
||||||
path: Option<String>,
|
path: Option<String>,
|
||||||
settings: &str,
|
settings: &str,
|
||||||
) -> Result<JsValue, String> {
|
) -> Result<JsValue, JsValue> {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
|
self.execute_typed(program_ast_json, path, settings)
|
||||||
|
.await
|
||||||
let ctx = self.create_executor_ctx(settings, path, false)?;
|
.and_then(|outcome| JsValue::from_serde(&outcome).map_err(|e| {
|
||||||
match ctx.run_with_caching(program).await {
|
|
||||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
||||||
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
||||||
Ok(outcome) => JsValue::from_serde(&outcome).map_err(|e| e.to_string()),
|
KclErrorWithOutputs::no_outputs(KclError::internal(
|
||||||
Err(err) => Err(serde_json::to_string(&err).map_err(|serde_err| serde_err.to_string())?),
|
format!("Could not serialize successful KCL result. This is a bug in KCL and not in your code, please report this to Zoo. Details: {e}"),
|
||||||
|
))}))
|
||||||
|
.map_err(|e: KclErrorWithOutputs| JsValue::from_serde(&e).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn execute_typed(
|
||||||
|
&self,
|
||||||
|
program_ast_json: &str,
|
||||||
|
path: Option<String>,
|
||||||
|
settings: &str,
|
||||||
|
) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||||
|
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| {
|
||||||
|
let err = KclError::internal(
|
||||||
|
format!("Could not deserialize KCL AST. This is a bug in KCL and not in your code, please report this to Zoo. Details: {e}"),
|
||||||
|
);
|
||||||
|
KclErrorWithOutputs::no_outputs(err)
|
||||||
|
})?;
|
||||||
|
let ctx = self
|
||||||
|
.create_executor_ctx(settings, path, false)
|
||||||
|
.map_err(|e| KclErrorWithOutputs::no_outputs(KclError::internal(
|
||||||
|
format!("Could not create KCL executor context. This is a bug in KCL and not in your code, please report this to Zoo. Details: {e}"),
|
||||||
|
)))?;
|
||||||
|
ctx.run_with_caching(program).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset the scene and bust the cache.
|
/// Reset the scene and bust the cache.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.86"
|
channel = "1.87"
|
||||||
components = ["clippy", "rustfmt"]
|
components = ["clippy", "rustfmt"]
|
||||||
|
@ -1315,7 +1315,7 @@ export class CameraControls {
|
|||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
initialInteractionType === 'rotate' &&
|
initialInteractionType === 'rotate' &&
|
||||||
this.engineCommandManager.settings.cameraOrbit === 'trackball'
|
this.getSettings?.().modeling.cameraOrbit.current === 'trackball'
|
||||||
) {
|
) {
|
||||||
return 'rotatetrackball'
|
return 'rotatetrackball'
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { isDesktop } from '@src/lib/isDesktop'
|
||||||
import { PATHS } from '@src/lib/paths'
|
import { PATHS } from '@src/lib/paths'
|
||||||
|
import { IMMEDIATE_SIGN_IN_IF_NECESSARY_QUERY_PARAM } from '@src/lib/constants'
|
||||||
import { useAuthState } from '@src/lib/singletons'
|
import { useAuthState } from '@src/lib/singletons'
|
||||||
|
import { generateSignInUrl } from '@src/routes/utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple hook that listens to the auth state of the app and navigates
|
* A simple hook that listens to the auth state of the app and navigates
|
||||||
@ -12,6 +15,10 @@ export function useAuthNavigation() {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const authState = useAuthState()
|
const authState = useAuthState()
|
||||||
|
const [searchParams] = useSearchParams()
|
||||||
|
const requestingImmediateSignInIfNecessary = searchParams.has(
|
||||||
|
IMMEDIATE_SIGN_IN_IF_NECESSARY_QUERY_PARAM
|
||||||
|
)
|
||||||
|
|
||||||
// Subscribe to the auth state of the app and navigate accordingly.
|
// Subscribe to the auth state of the app and navigate accordingly.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -24,6 +31,11 @@ export function useAuthNavigation() {
|
|||||||
authState.matches('loggedOut') &&
|
authState.matches('loggedOut') &&
|
||||||
!location.pathname.includes(PATHS.SIGN_IN)
|
!location.pathname.includes(PATHS.SIGN_IN)
|
||||||
) {
|
) {
|
||||||
|
if (requestingImmediateSignInIfNecessary && !isDesktop()) {
|
||||||
|
window.location.href = generateSignInUrl()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
navigate(PATHS.SIGN_IN + (location.search || ''))
|
navigate(PATHS.SIGN_IN + (location.search || ''))
|
||||||
}
|
}
|
||||||
}, [authState, location.pathname])
|
}, [authState, location.pathname])
|
||||||
|
@ -389,7 +389,20 @@ export function sketchFromKclValue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const errFromErrWithOutputs = (e: any): KCLError => {
|
export const errFromErrWithOutputs = (e: any): KCLError => {
|
||||||
const parsed: KclErrorWithOutputs = JSON.parse(e.toString())
|
// `e` is any, so let's figure out something useful to do with it.
|
||||||
|
const parsed: KclErrorWithOutputs = (() => {
|
||||||
|
// No need to parse, it's already an object.
|
||||||
|
if (typeof e === 'object') {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
// It's a string, so parse it.
|
||||||
|
if (typeof e === 'string') {
|
||||||
|
return JSON.parse(e)
|
||||||
|
}
|
||||||
|
// It can be converted to a string, then parsed.
|
||||||
|
return JSON.parse(e.toString())
|
||||||
|
})()
|
||||||
|
|
||||||
return new KCLError(
|
return new KCLError(
|
||||||
parsed.error.kind,
|
parsed.error.kind,
|
||||||
parsed.error.details.msg,
|
parsed.error.details.msg,
|
||||||
|
@ -217,3 +217,6 @@ export const POOL_QUERY_PARAM = 'pool'
|
|||||||
* @deprecated: supporting old share links with this. For new command URLs, use "cmd"
|
* @deprecated: supporting old share links with this. For new command URLs, use "cmd"
|
||||||
*/
|
*/
|
||||||
export const CREATE_FILE_URL_PARAM = 'create-file'
|
export const CREATE_FILE_URL_PARAM = 'create-file'
|
||||||
|
/** A query parameter to skip the sign-on view if unnecessary. */
|
||||||
|
export const IMMEDIATE_SIGN_IN_IF_NECESSARY_QUERY_PARAM =
|
||||||
|
'immediate-sign-in-if-necessary'
|
||||||
|
@ -10,12 +10,11 @@ import { VITE_KC_API_BASE_URL, VITE_KC_SITE_BASE_URL } from '@src/env'
|
|||||||
import { APP_NAME } from '@src/lib/constants'
|
import { APP_NAME } from '@src/lib/constants'
|
||||||
import { isDesktop } from '@src/lib/isDesktop'
|
import { isDesktop } from '@src/lib/isDesktop'
|
||||||
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
||||||
import { PATHS } from '@src/lib/paths'
|
|
||||||
import { Themes, getSystemTheme } from '@src/lib/theme'
|
import { Themes, getSystemTheme } from '@src/lib/theme'
|
||||||
import { reportRejection } from '@src/lib/trap'
|
import { reportRejection } from '@src/lib/trap'
|
||||||
import { toSync } from '@src/lib/utils'
|
import { toSync } from '@src/lib/utils'
|
||||||
import { authActor, useSettings } from '@src/lib/singletons'
|
import { authActor, useSettings } from '@src/lib/singletons'
|
||||||
import { APP_VERSION } from '@src/routes/utils'
|
import { APP_VERSION, generateSignInUrl } from '@src/routes/utils'
|
||||||
|
|
||||||
const subtleBorder =
|
const subtleBorder =
|
||||||
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
||||||
@ -36,11 +35,7 @@ const SignIn = () => {
|
|||||||
const {
|
const {
|
||||||
app: { theme },
|
app: { theme },
|
||||||
} = useSettings()
|
} = useSettings()
|
||||||
const signInUrl = `${VITE_KC_SITE_BASE_URL}${
|
const signInUrl = generateSignInUrl()
|
||||||
PATHS.SIGN_IN
|
|
||||||
}?callbackUrl=${encodeURIComponent(
|
|
||||||
typeof window !== 'undefined' && window.location.href.replace('signin', '')
|
|
||||||
)}`
|
|
||||||
const kclSampleUrl = `${VITE_KC_SITE_BASE_URL}/docs/kcl-samples/car-wheel-assembly`
|
const kclSampleUrl = `${VITE_KC_SITE_BASE_URL}/docs/kcl-samples/car-wheel-assembly`
|
||||||
|
|
||||||
const getThemeText = useCallback(
|
const getThemeText = useCallback(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { NODE_ENV } from '@src/env'
|
import { NODE_ENV, VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||||
import { isDesktop } from '@src/lib/isDesktop'
|
import { isDesktop } from '@src/lib/isDesktop'
|
||||||
import { IS_PLAYWRIGHT_KEY } from '@src/lib/constants'
|
import { IS_PLAYWRIGHT_KEY } from '@src/lib/constants'
|
||||||
|
import { PATHS } from '@src/lib/paths'
|
||||||
|
|
||||||
const isTestEnv = window?.localStorage.getItem(IS_PLAYWRIGHT_KEY) === 'true'
|
const isTestEnv = window?.localStorage.getItem(IS_PLAYWRIGHT_KEY) === 'true'
|
||||||
|
|
||||||
@ -27,3 +28,11 @@ export function getReleaseUrl(version: string = APP_VERSION) {
|
|||||||
|
|
||||||
return `https://github.com/KittyCAD/modeling-app/releases/tag/v${version}`
|
return `https://github.com/KittyCAD/modeling-app/releases/tag/v${version}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateSignInUrl() {
|
||||||
|
return `${VITE_KC_SITE_BASE_URL}${
|
||||||
|
PATHS.SIGN_IN
|
||||||
|
}?callbackUrl=${encodeURIComponent(
|
||||||
|
typeof window !== 'undefined' && window.location.href.replace('signin', '')
|
||||||
|
)}`
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user