Compare commits
5 Commits
parser-in-
...
v0.18.0
Author | SHA1 | Date | |
---|---|---|---|
0b235dc1cd | |||
0857415793 | |||
1da4fd03ef | |||
39d84c12ab | |||
537d86c8ff |
@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ["@babel/preset-env"],
|
presets: ['@babel/preset-env'],
|
||||||
}
|
}
|
||||||
|
@ -328,9 +328,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
/* Ignore this test for now since its causing engine to crash
|
test('if your kcl gets an error from the engine it is inlined', async ({
|
||||||
*
|
|
||||||
* test('if your kcl gets an error from the engine it is inlined', async ({
|
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
@ -349,7 +347,7 @@ const sketch001 = startSketchOn(box, "revolveAxis")
|
|||||||
|> startProfileAt([5, 10], %)
|
|> startProfileAt([5, 10], %)
|
||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> line([2, 0], %)
|
|> line([2, 0], %)
|
||||||
|> line([0, 10], %)
|
|> line([0, -10], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> revolve({
|
|> revolve({
|
||||||
axis: getEdge('revolveAxis', box),
|
axis: getEdge('revolveAxis', box),
|
||||||
@ -378,7 +376,7 @@ angle: 90
|
|||||||
'sketch profile must lie entirely on one side of the revolution axis'
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
)
|
)
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})*/
|
})
|
||||||
|
|
||||||
test('executes on load', async ({ page }) => {
|
test('executes on load', async ({ page }) => {
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
@ -1673,3 +1671,42 @@ test('Can code mod a line length', async ({ page }) => {
|
|||||||
`const length001 = 20const part001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-length001, %)`
|
`const length001 = 20const part001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-length001, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Extrude from command bar selects extrude line after', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const part001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> xLine(-20, %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Click the line of code for xLine.
|
||||||
|
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
|
` |> extrude(5 + 7, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.17.3",
|
"version": "0.18.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.16.0",
|
"@codemirror/autocomplete": "^6.16.0",
|
||||||
@ -8,7 +8,7 @@
|
|||||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.18",
|
"@headlessui/react": "^1.7.19",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "^0.0.58",
|
"@kittycad/lib": "^0.0.58",
|
||||||
"@lezer/javascript": "^1.4.9",
|
"@lezer/javascript": "^1.4.9",
|
||||||
@ -84,8 +84,8 @@
|
|||||||
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||||
"fmt": "prettier --write ./src && prettier --write ./e2e",
|
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e",
|
||||||
"fmt-check": "prettier --check ./src && prettier --check ./e2e",
|
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e",
|
||||||
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||||
"build:wasm": "(cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
"build:wasm": "(cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||||
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
|
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
|
||||||
@ -132,6 +132,7 @@
|
|||||||
"@types/wicg-file-system-access": "^2023.10.5",
|
"@types/wicg-file-system-access": "^2023.10.5",
|
||||||
"@types/ws": "^8.5.10",
|
"@types/ws": "^8.5.10",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"@vitest/web-worker": "^1.5.0",
|
||||||
"@wdio/cli": "^8.24.3",
|
"@wdio/cli": "^8.24.3",
|
||||||
"@wdio/globals": "^8.36.0",
|
"@wdio/globals": "^8.36.0",
|
||||||
"@wdio/local-runner": "^8.36.0",
|
"@wdio/local-runner": "^8.36.0",
|
||||||
|
@ -49,8 +49,6 @@ export default defineConfig({
|
|||||||
// use: { ...devices['Desktop Chrome'] },
|
// use: { ...devices['Desktop Chrome'] },
|
||||||
// },
|
// },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Test against mobile viewports. */
|
/* Test against mobile viewports. */
|
||||||
// {
|
// {
|
||||||
// name: 'Mobile Chrome',
|
// name: 'Mobile Chrome',
|
||||||
|
@ -55,5 +55,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"productName": "Zoo Modeling App",
|
"productName": "Zoo Modeling App",
|
||||||
"version": "0.17.3"
|
"version": "0.18.0"
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
|||||||
|
|
||||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { useStore } from 'useStore'
|
|
||||||
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
||||||
import { ReactCameraProperties } from './CameraControls'
|
import { ReactCameraProperties } from './CameraControls'
|
||||||
import { throttle } from 'lib/utils'
|
import { throttle } from 'lib/utils'
|
||||||
@ -47,10 +46,6 @@ export const ClientSideScene = ({
|
|||||||
const canvasRef = useRef<HTMLDivElement>(null)
|
const canvasRef = useRef<HTMLDivElement>(null)
|
||||||
const { state, send, context } = useModelingContext()
|
const { state, send, context } = useModelingContext()
|
||||||
const { hideClient, hideServer } = useShouldHideScene()
|
const { hideClient, hideServer } = useShouldHideScene()
|
||||||
const { setHighlightRange } = useStore((s) => ({
|
|
||||||
setHighlightRange: s.setHighlightRange,
|
|
||||||
highlightRange: s.highlightRange,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Listen for changes to the camera controls setting
|
// Listen for changes to the camera controls setting
|
||||||
// and update the client-side scene's controls accordingly.
|
// and update the client-side scene's controls accordingly.
|
||||||
@ -69,7 +64,6 @@ export const ClientSideScene = ({
|
|||||||
const canvas = canvasRef.current
|
const canvas = canvasRef.current
|
||||||
canvas.appendChild(sceneInfra.renderer.domElement)
|
canvas.appendChild(sceneInfra.renderer.domElement)
|
||||||
sceneInfra.animate()
|
sceneInfra.animate()
|
||||||
sceneInfra.setHighlightCallback(setHighlightRange)
|
|
||||||
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
|
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
|
||||||
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
||||||
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
||||||
|
@ -57,6 +57,7 @@ import {
|
|||||||
kclManager,
|
kclManager,
|
||||||
sceneInfra,
|
sceneInfra,
|
||||||
codeManager,
|
codeManager,
|
||||||
|
editorManager,
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { executeAst, useStore } from 'useStore'
|
import { executeAst, useStore } from 'useStore'
|
||||||
@ -1423,7 +1424,7 @@ export class SceneEntities {
|
|||||||
parent.userData.pathToNode,
|
parent.userData.pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
).node
|
).node
|
||||||
sceneInfra.highlightCallback([node.start, node.end])
|
editorManager.setHighlightRange([node.start, node.end])
|
||||||
const yellow = 0xffff00
|
const yellow = 0xffff00
|
||||||
colorSegment(selected, yellow)
|
colorSegment(selected, yellow)
|
||||||
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||||
@ -1459,10 +1460,10 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sceneInfra.highlightCallback([0, 0])
|
editorManager.setHighlightRange([0, 0])
|
||||||
},
|
},
|
||||||
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
||||||
sceneInfra.highlightCallback([0, 0])
|
editorManager.setHighlightRange([0, 0])
|
||||||
const parent = getParentGroup(selected, [
|
const parent = getParentGroup(selected, [
|
||||||
STRAIGHT_SEGMENT,
|
STRAIGHT_SEGMENT,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
|
@ -24,7 +24,6 @@ import {
|
|||||||
import { compareVec2Epsilon2 } from 'lang/std/sketch'
|
import { compareVec2Epsilon2 } from 'lang/std/sketch'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import * as TWEEN from '@tweenjs/tween.js'
|
import * as TWEEN from '@tweenjs/tween.js'
|
||||||
import { SourceRange } from 'lang/wasm'
|
|
||||||
import { Axis } from 'lib/selections'
|
import { Axis } from 'lib/selections'
|
||||||
import { type BaseUnit } from 'lib/settings/settingsTypes'
|
import { type BaseUnit } from 'lib/settings/settingsTypes'
|
||||||
import { CameraControls } from './CameraControls'
|
import { CameraControls } from './CameraControls'
|
||||||
@ -149,10 +148,6 @@ export class SceneInfra {
|
|||||||
onMouseLeave: () => {},
|
onMouseLeave: () => {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
highlightCallback: (a: SourceRange) => void = () => {}
|
|
||||||
setHighlightCallback(cb: (a: SourceRange) => void) {
|
|
||||||
this.highlightCallback = cb
|
|
||||||
}
|
|
||||||
|
|
||||||
modelingSend: SendType = (() => {}) as any
|
modelingSend: SendType = (() => {}) as any
|
||||||
setSend(send: SendType) {
|
setSend(send: SendType) {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { editorManager, kclManager } from 'lib/singletons'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useStore } from 'useStore'
|
|
||||||
|
|
||||||
export function AstExplorer() {
|
export function AstExplorer() {
|
||||||
const setHighlightRange = useStore((s) => s.setHighlightRange)
|
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
// TODO maybe need to have callback to make sure it stays in sync
|
// TODO maybe need to have callback to make sure it stays in sync
|
||||||
@ -42,7 +40,7 @@ export function AstExplorer() {
|
|||||||
<div
|
<div
|
||||||
className="h-full relative"
|
className="h-full relative"
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
setHighlightRange([0, 0])
|
editorManager.setHighlightRange([0, 0])
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<pre className="text-xs">
|
<pre className="text-xs">
|
||||||
@ -88,7 +86,6 @@ function DisplayObj({
|
|||||||
filterKeys: string[]
|
filterKeys: string[]
|
||||||
node: any
|
node: any
|
||||||
}) {
|
}) {
|
||||||
const setHighlightRange = useStore((s) => s.setHighlightRange)
|
|
||||||
const { send } = useModelingContext()
|
const { send } = useModelingContext()
|
||||||
const ref = useRef<HTMLPreElement>(null)
|
const ref = useRef<HTMLPreElement>(null)
|
||||||
const [hasCursor, setHasCursor] = useState(false)
|
const [hasCursor, setHasCursor] = useState(false)
|
||||||
@ -112,12 +109,12 @@ function DisplayObj({
|
|||||||
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
||||||
}`}
|
}`}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
setHighlightRange([obj?.start || 0, obj.end])
|
editorManager.setHighlightRange([obj?.start || 0, obj.end])
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}}
|
}}
|
||||||
onMouseMove={(e) => {
|
onMouseMove={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
setHighlightRange([obj?.start || 0, obj.end])
|
editorManager.setHighlightRange([obj?.start || 0, obj.end])
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
send({
|
send({
|
||||||
|
@ -137,10 +137,10 @@ export function useCalc({
|
|||||||
setAvailableVarInfo(varInfo)
|
setAvailableVarInfo(varInfo)
|
||||||
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const code = `const __result__ = ${value}`
|
const code = `const __result__ = ${value}`
|
||||||
parse(code).then((ast) => {
|
const ast = parse(code)
|
||||||
const _programMem: any = { root: {}, return: null }
|
const _programMem: any = { root: {}, return: null }
|
||||||
availableVarInfo.variables.forEach(({ key, value }) => {
|
availableVarInfo.variables.forEach(({ key, value }) => {
|
||||||
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
||||||
@ -165,7 +165,6 @@ export function useCalc({
|
|||||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||||
init && setValueNode(init)
|
init && setValueNode(init)
|
||||||
})
|
})
|
||||||
})
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setCalcResult('NAN')
|
setCalcResult('NAN')
|
||||||
setValueNode(null)
|
setValueNode(null)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useMachine } from '@xstate/react'
|
import { useMachine } from '@xstate/react'
|
||||||
|
import { editorManager } from 'lib/singletons'
|
||||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||||
import { createContext } from 'react'
|
import { createContext, useEffect } from 'react'
|
||||||
import { EventFrom, StateFrom } from 'xstate'
|
import { EventFrom, StateFrom } from 'xstate'
|
||||||
|
|
||||||
type CommandsContextType = {
|
type CommandsContextType = {
|
||||||
@ -30,6 +31,10 @@ export const CommandBarProvider = ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
editorManager.setCommandBarSend(commandBarSend)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommandsContext.Provider
|
<CommandsContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
sceneInfra,
|
sceneInfra,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
codeManager,
|
codeManager,
|
||||||
|
editorManager,
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
||||||
import {
|
import {
|
||||||
@ -98,17 +99,6 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
useHotkeys('meta + shift + .', () => coreDump(coreDumpManager, true))
|
useHotkeys('meta + shift + .', () => coreDump(coreDumpManager, true))
|
||||||
|
|
||||||
const {
|
|
||||||
isShiftDown,
|
|
||||||
editorView,
|
|
||||||
setLastCodeMirrorSelectionUpdatedFromScene,
|
|
||||||
} = useStore((s) => ({
|
|
||||||
isShiftDown: s.isShiftDown,
|
|
||||||
editorView: s.editorView,
|
|
||||||
setLastCodeMirrorSelectionUpdatedFromScene:
|
|
||||||
s.setLastCodeMirrorSelectionUpdatedFromScene,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Settings machine setup
|
// Settings machine setup
|
||||||
// const retrievedSettings = useRef(
|
// const retrievedSettings = useRef(
|
||||||
// localStorage?.getItem(MODELING_PERSIST_KEY) || '{}'
|
// localStorage?.getItem(MODELING_PERSIST_KEY) || '{}'
|
||||||
@ -135,29 +125,33 @@ export const ModelingMachineProvider = ({
|
|||||||
'Set selection': assign(({ selectionRanges }, event) => {
|
'Set selection': assign(({ selectionRanges }, event) => {
|
||||||
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
|
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
|
||||||
const setSelections = event.data
|
const setSelections = event.data
|
||||||
if (!editorView) return {}
|
if (!editorManager.editorView) return {}
|
||||||
const dispatchSelection = (selection?: EditorSelection) => {
|
const dispatchSelection = (selection?: EditorSelection) => {
|
||||||
if (!selection) return // TODO less of hack for the below please
|
if (!selection) return // TODO less of hack for the below please
|
||||||
setLastCodeMirrorSelectionUpdatedFromScene(Date.now())
|
editorManager.lastSelectionEvent = Date.now()
|
||||||
setTimeout(() => editorView.dispatch({ selection }))
|
setTimeout(() => {
|
||||||
|
if (editorManager.editorView) {
|
||||||
|
editorManager.editorView.dispatch({ selection })
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
let selections: Selections = {
|
let selections: Selections = {
|
||||||
codeBasedSelections: [],
|
codeBasedSelections: [],
|
||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
if (setSelections.selectionType === 'singleCodeCursor') {
|
if (setSelections.selectionType === 'singleCodeCursor') {
|
||||||
if (!setSelections.selection && isShiftDown) {
|
if (!setSelections.selection && editorManager.isShiftDown) {
|
||||||
} else if (!setSelections.selection && !isShiftDown) {
|
} else if (!setSelections.selection && !editorManager.isShiftDown) {
|
||||||
selections = {
|
selections = {
|
||||||
codeBasedSelections: [],
|
codeBasedSelections: [],
|
||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
} else if (setSelections.selection && !isShiftDown) {
|
} else if (setSelections.selection && !editorManager.isShiftDown) {
|
||||||
selections = {
|
selections = {
|
||||||
codeBasedSelections: [setSelections.selection],
|
codeBasedSelections: [setSelections.selection],
|
||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
} else if (setSelections.selection && isShiftDown) {
|
} else if (setSelections.selection && editorManager.isShiftDown) {
|
||||||
selections = {
|
selections = {
|
||||||
codeBasedSelections: [
|
codeBasedSelections: [
|
||||||
...selectionRanges.codeBasedSelections,
|
...selectionRanges.codeBasedSelections,
|
||||||
@ -180,6 +174,7 @@ export const ModelingMachineProvider = ({
|
|||||||
engineCommandManager.sendSceneCommand(event)
|
engineCommandManager.sendSceneCommand(event)
|
||||||
)
|
)
|
||||||
updateSceneObjectColors()
|
updateSceneObjectColors()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectionRanges: selections,
|
selectionRanges: selections,
|
||||||
}
|
}
|
||||||
@ -192,7 +187,7 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (setSelections.selectionType === 'otherSelection') {
|
if (setSelections.selectionType === 'otherSelection') {
|
||||||
if (isShiftDown) {
|
if (editorManager.isShiftDown) {
|
||||||
selections = {
|
selections = {
|
||||||
codeBasedSelections: selectionRanges.codeBasedSelections,
|
codeBasedSelections: selectionRanges.codeBasedSelections,
|
||||||
otherSelections: [setSelections.selection],
|
otherSelections: [setSelections.selection],
|
||||||
@ -516,6 +511,19 @@ export const ModelingMachineProvider = ({
|
|||||||
})
|
})
|
||||||
}, [modelingSend])
|
}, [modelingSend])
|
||||||
|
|
||||||
|
// Give the state back to the editorManager.
|
||||||
|
useEffect(() => {
|
||||||
|
editorManager.modelingSend = modelingSend
|
||||||
|
}, [modelingSend])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
editorManager.modelingEvent = modelingState.event
|
||||||
|
}, [modelingState.event])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
editorManager.selectionRanges = modelingState.context.selectionRanges
|
||||||
|
}, [modelingState.context.selectionRanges])
|
||||||
|
|
||||||
useStateMachineCommands({
|
useStateMachineCommands({
|
||||||
machineId: 'modeling',
|
machineId: 'modeling',
|
||||||
state: modelingState,
|
state: modelingState,
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
import { undo, redo } from '@codemirror/commands'
|
|
||||||
import ReactCodeMirror from '@uiw/react-codemirror'
|
import ReactCodeMirror from '@uiw/react-codemirror'
|
||||||
import { TEST } from 'env'
|
import { TEST } from 'env'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
|
||||||
import { Themes, getSystemTheme } from 'lib/theme'
|
import { Themes, getSystemTheme } from 'lib/theme'
|
||||||
import { useEffect, useMemo, useRef } from 'react'
|
import { useEffect, useMemo } from 'react'
|
||||||
import { useStore } from 'useStore'
|
|
||||||
import { processCodeMirrorRanges } from 'lib/selections'
|
|
||||||
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
|
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
|
||||||
import { lineHighlightField } from 'editor/highlightextension'
|
import { lineHighlightField } from 'editor/highlightextension'
|
||||||
import { roundOff } from 'lib/utils'
|
import { roundOff } from 'lib/utils'
|
||||||
@ -29,7 +24,7 @@ import {
|
|||||||
historyKeymap,
|
historyKeymap,
|
||||||
history,
|
history,
|
||||||
} from '@codemirror/commands'
|
} from '@codemirror/commands'
|
||||||
import { lintGutter, lintKeymap, linter } from '@codemirror/lint'
|
import { lintGutter, lintKeymap } from '@codemirror/lint'
|
||||||
import {
|
import {
|
||||||
foldGutter,
|
foldGutter,
|
||||||
foldKeymap,
|
foldKeymap,
|
||||||
@ -39,25 +34,20 @@ import {
|
|||||||
syntaxHighlighting,
|
syntaxHighlighting,
|
||||||
defaultHighlightStyle,
|
defaultHighlightStyle,
|
||||||
} from '@codemirror/language'
|
} from '@codemirror/language'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
|
||||||
import interact from '@replit/codemirror-interact'
|
import interact from '@replit/codemirror-interact'
|
||||||
import { engineCommandManager, sceneInfra, kclManager } from 'lib/singletons'
|
import { kclManager, editorManager, codeManager } from 'lib/singletons'
|
||||||
import { useKclContext } from 'lang/KclProvider'
|
|
||||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import makeUrlPathRelative from 'lib/makeUrlPathRelative'
|
import makeUrlPathRelative from 'lib/makeUrlPathRelative'
|
||||||
import { useLspContext } from 'components/LspProvider'
|
import { useLspContext } from 'components/LspProvider'
|
||||||
import { Prec, EditorState, Extension, SelectionRange } from '@codemirror/state'
|
import { Prec, EditorState, Extension } from '@codemirror/state'
|
||||||
import {
|
import {
|
||||||
closeBrackets,
|
closeBrackets,
|
||||||
closeBracketsKeymap,
|
closeBracketsKeymap,
|
||||||
completionKeymap,
|
completionKeymap,
|
||||||
hasNextSnippetField,
|
|
||||||
} from '@codemirror/autocomplete'
|
} from '@codemirror/autocomplete'
|
||||||
import { kclErrorsToDiagnostics } from 'lang/errors'
|
|
||||||
|
|
||||||
export const editorShortcutMeta = {
|
export const editorShortcutMeta = {
|
||||||
formatCode: {
|
formatCode: {
|
||||||
@ -77,13 +67,6 @@ export const KclEditorPane = () => {
|
|||||||
context.app.theme.current === Themes.System
|
context.app.theme.current === Themes.System
|
||||||
? getSystemTheme()
|
? getSystemTheme()
|
||||||
: context.app.theme.current
|
: context.app.theme.current
|
||||||
const { editorView, setEditorView, isShiftDown } = useStore((s) => ({
|
|
||||||
editorView: s.editorView,
|
|
||||||
setEditorView: s.setEditorView,
|
|
||||||
isShiftDown: s.isShiftDown,
|
|
||||||
}))
|
|
||||||
const { editorCode, errors } = useKclContext()
|
|
||||||
const lastEvent = useRef({ event: '', time: Date.now() })
|
|
||||||
const { copilotLSP, kclLSP } = useLspContext()
|
const { copilotLSP, kclLSP } = useLspContext()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
@ -96,90 +79,15 @@ export const KclEditorPane = () => {
|
|||||||
|
|
||||||
useHotkeys('mod+z', (e) => {
|
useHotkeys('mod+z', (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (editorView) {
|
editorManager.undo()
|
||||||
undo(editorView)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
useHotkeys('mod+shift+z', (e) => {
|
useHotkeys('mod+shift+z', (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (editorView) {
|
editorManager.redo()
|
||||||
redo(editorView)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const textWrapping = context.textEditor.textWrapping
|
||||||
context: { selectionRanges },
|
const cursorBlinking = context.textEditor.blinkingCursor
|
||||||
send,
|
|
||||||
state,
|
|
||||||
} = useModelingContext()
|
|
||||||
|
|
||||||
const { settings } = useSettingsAuthContext()
|
|
||||||
const textWrapping = settings.context.textEditor.textWrapping
|
|
||||||
const cursorBlinking = settings.context.textEditor.blinkingCursor
|
|
||||||
const { commandBarSend } = useCommandsContext()
|
|
||||||
const { enable: convertEnabled, handleClick: convertCallback } =
|
|
||||||
useConvertToVariable()
|
|
||||||
|
|
||||||
const lastSelection = useRef('')
|
|
||||||
const onUpdate = (viewUpdate: ViewUpdate) => {
|
|
||||||
// If we are just fucking around in a snippet, return early and don't
|
|
||||||
// trigger stuff below that might cause the component to re-render.
|
|
||||||
// Otherwise we will not be able to tab thru the snippet portions.
|
|
||||||
// We explicitly dont check HasPrevSnippetField because we always add
|
|
||||||
// a ${} to the end of the function so that's fine.
|
|
||||||
if (hasNextSnippetField(viewUpdate.view.state)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!editorView) {
|
|
||||||
setEditorView(viewUpdate.view)
|
|
||||||
}
|
|
||||||
const selString = stringifyRanges(
|
|
||||||
viewUpdate?.state?.selection?.ranges || []
|
|
||||||
)
|
|
||||||
if (selString === lastSelection.current) {
|
|
||||||
// onUpdate is noisy and is fired a lot by extensions
|
|
||||||
// since we're only interested in selections changes we can ignore most of these.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lastSelection.current = selString
|
|
||||||
|
|
||||||
if (
|
|
||||||
// TODO find a less lazy way of getting the last
|
|
||||||
Date.now() - useStore.getState().lastCodeMirrorSelectionUpdatedFromScene <
|
|
||||||
150
|
|
||||||
)
|
|
||||||
return // update triggered by scene selection
|
|
||||||
if (sceneInfra.selected) return // mid drag
|
|
||||||
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
|
||||||
'Equip Line tool',
|
|
||||||
'Equip tangential arc to',
|
|
||||||
]
|
|
||||||
if (ignoreEvents.includes(state.event.type)) return
|
|
||||||
const eventInfo = processCodeMirrorRanges({
|
|
||||||
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
|
||||||
selectionRanges,
|
|
||||||
isShiftDown,
|
|
||||||
})
|
|
||||||
if (!eventInfo) return
|
|
||||||
const deterministicEventInfo = {
|
|
||||||
...eventInfo,
|
|
||||||
engineEvents: eventInfo.engineEvents.map((e) => ({
|
|
||||||
...e,
|
|
||||||
cmd_id: 'static',
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
const stringEvent = JSON.stringify(deterministicEventInfo)
|
|
||||||
if (
|
|
||||||
stringEvent === lastEvent.current.event &&
|
|
||||||
Date.now() - lastEvent.current.time < 500
|
|
||||||
)
|
|
||||||
return // don't repeat events
|
|
||||||
lastEvent.current = { event: stringEvent, time: Date.now() }
|
|
||||||
send(eventInfo.modelingEvent)
|
|
||||||
eventInfo.engineEvents.forEach((event) =>
|
|
||||||
engineCommandManager.sendSceneCommand(event)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const editorExtensions = useMemo(() => {
|
const editorExtensions = useMemo(() => {
|
||||||
const extensions = [
|
const extensions = [
|
||||||
@ -202,7 +110,7 @@ export const KclEditorPane = () => {
|
|||||||
{
|
{
|
||||||
key: 'Meta-k',
|
key: 'Meta-k',
|
||||||
run: () => {
|
run: () => {
|
||||||
commandBarSend({ type: 'Open' })
|
editorManager.commandBarSend({ type: 'Open' })
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -216,11 +124,7 @@ export const KclEditorPane = () => {
|
|||||||
{
|
{
|
||||||
key: editorShortcutMeta.convertToVariable.codeMirror,
|
key: editorShortcutMeta.convertToVariable.codeMirror,
|
||||||
run: () => {
|
run: () => {
|
||||||
if (convertEnabled) {
|
return editorManager.convertToVariable()
|
||||||
convertCallback()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
@ -233,9 +137,6 @@ export const KclEditorPane = () => {
|
|||||||
if (!TEST) {
|
if (!TEST) {
|
||||||
extensions.push(
|
extensions.push(
|
||||||
lintGutter(),
|
lintGutter(),
|
||||||
linter((_view: EditorView) => {
|
|
||||||
return kclErrorsToDiagnostics(errors)
|
|
||||||
}),
|
|
||||||
lineNumbers(),
|
lineNumbers(),
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
highlightSpecialChars(),
|
highlightSpecialChars(),
|
||||||
@ -288,13 +189,10 @@ export const KclEditorPane = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return extensions
|
return extensions
|
||||||
}, [
|
}, [kclLSP, copilotLSP, textWrapping.current, cursorBlinking.current])
|
||||||
kclLSP,
|
|
||||||
copilotLSP,
|
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
textWrapping.current,
|
const updateDelay = 100
|
||||||
cursorBlinking.current,
|
|
||||||
convertCallback,
|
|
||||||
])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -302,18 +200,26 @@ export const KclEditorPane = () => {
|
|||||||
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
|
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
|
||||||
>
|
>
|
||||||
<ReactCodeMirror
|
<ReactCodeMirror
|
||||||
value={editorCode}
|
value={codeManager.code}
|
||||||
extensions={editorExtensions}
|
extensions={editorExtensions}
|
||||||
onUpdate={onUpdate}
|
|
||||||
theme={theme}
|
theme={theme}
|
||||||
onCreateEditor={(_editorView) => setEditorView(_editorView)}
|
onCreateEditor={(_editorView) =>
|
||||||
|
editorManager.setEditorView(_editorView)
|
||||||
|
}
|
||||||
|
onUpdate={(view: ViewUpdate) => {
|
||||||
|
// debounce the view update.
|
||||||
|
// otherwise it is laggy for typing.
|
||||||
|
if (debounceTimer) {
|
||||||
|
clearTimeout(debounceTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
debounceTimer = setTimeout(() => {
|
||||||
|
editorManager.handleOnViewUpdate(view)
|
||||||
|
}, updateDelay)
|
||||||
|
}}
|
||||||
indentWithTab={false}
|
indentWithTab={false}
|
||||||
basicSetup={false}
|
basicSetup={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringifyRanges(ranges: readonly SelectionRange[]): string {
|
|
||||||
return ranges.map(({ to, from }) => `${to}->${from}`).join('&')
|
|
||||||
}
|
|
||||||
|
@ -2,7 +2,9 @@ import { processMemory } from './MemoryPane'
|
|||||||
import { enginelessExecutor } from '../../../lib/testHelpers'
|
import { enginelessExecutor } from '../../../lib/testHelpers'
|
||||||
import { initPromise, parse } from '../../../lang/wasm'
|
import { initPromise, parse } from '../../../lang/wasm'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('processMemory', () => {
|
describe('processMemory', () => {
|
||||||
it('should grab the values and remove and geo data', async () => {
|
it('should grab the values and remove and geo data', async () => {
|
||||||
@ -26,7 +28,7 @@ describe('processMemory', () => {
|
|||||||
|> lineTo([0.98, 5.16], %)
|
|> lineTo([0.98, 5.16], %)
|
||||||
|> lineTo([2.15, 4.32], %)
|
|> lineTo([2.15, 4.32], %)
|
||||||
// |> rx(90, %)`
|
// |> rx(90, %)`
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast, {
|
const programMemory = await enginelessExecutor(ast, {
|
||||||
root: {},
|
root: {},
|
||||||
return: null,
|
return: null,
|
||||||
|
238
src/editor/manager.ts
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
import { hasNextSnippetField } from '@codemirror/autocomplete'
|
||||||
|
import { EditorView, ViewUpdate } from '@codemirror/view'
|
||||||
|
import { EditorSelection, SelectionRange } from '@codemirror/state'
|
||||||
|
import { engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||||
|
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||||
|
import { Selections, processCodeMirrorRanges, Selection } from 'lib/selections'
|
||||||
|
import { undo, redo } from '@codemirror/commands'
|
||||||
|
import { CommandBarMachineEvent } from 'machines/commandBarMachine'
|
||||||
|
import { addLineHighlight } from './highlightextension'
|
||||||
|
import { setDiagnostics, Diagnostic } from '@codemirror/lint'
|
||||||
|
|
||||||
|
export default class EditorManager {
|
||||||
|
private _editorView: EditorView | null = null
|
||||||
|
|
||||||
|
private _isShiftDown: boolean = false
|
||||||
|
private _selectionRanges: Selections = {
|
||||||
|
otherSelections: [],
|
||||||
|
codeBasedSelections: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
private _lastSelectionEvent: number | null = null
|
||||||
|
private _lastSelection: string = ''
|
||||||
|
private _lastEvent: { event: string; time: number } | null = null
|
||||||
|
|
||||||
|
private _modelingSend: (eventInfo: ModelingMachineEvent) => void = () => {}
|
||||||
|
private _modelingEvent: ModelingMachineEvent | null = null
|
||||||
|
|
||||||
|
private _commandBarSend: (eventInfo: CommandBarMachineEvent) => void =
|
||||||
|
() => {}
|
||||||
|
|
||||||
|
private _convertToVariableEnabled: boolean = false
|
||||||
|
private _convertToVariableCallback: () => void = () => {}
|
||||||
|
|
||||||
|
private _highlightRange: [number, number] = [0, 0]
|
||||||
|
|
||||||
|
setEditorView(editorView: EditorView) {
|
||||||
|
this._editorView = editorView
|
||||||
|
}
|
||||||
|
|
||||||
|
get editorView(): EditorView | null {
|
||||||
|
return this._editorView
|
||||||
|
}
|
||||||
|
|
||||||
|
get isShiftDown(): boolean {
|
||||||
|
return this._isShiftDown
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsShiftDown(isShiftDown: boolean) {
|
||||||
|
this._isShiftDown = isShiftDown
|
||||||
|
}
|
||||||
|
|
||||||
|
set selectionRanges(selectionRanges: Selections) {
|
||||||
|
this._selectionRanges = selectionRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
set lastSelectionEvent(time: number) {
|
||||||
|
this._lastSelectionEvent = time
|
||||||
|
}
|
||||||
|
|
||||||
|
set modelingSend(send: (eventInfo: ModelingMachineEvent) => void) {
|
||||||
|
this._modelingSend = send
|
||||||
|
}
|
||||||
|
|
||||||
|
set modelingEvent(event: ModelingMachineEvent) {
|
||||||
|
this._modelingEvent = event
|
||||||
|
}
|
||||||
|
|
||||||
|
setCommandBarSend(send: (eventInfo: CommandBarMachineEvent) => void) {
|
||||||
|
this._commandBarSend = send
|
||||||
|
}
|
||||||
|
|
||||||
|
commandBarSend(eventInfo: CommandBarMachineEvent): void {
|
||||||
|
return this._commandBarSend(eventInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
get highlightRange(): [number, number] {
|
||||||
|
return this._highlightRange
|
||||||
|
}
|
||||||
|
|
||||||
|
setHighlightRange(selection: Selection['range']): void {
|
||||||
|
this._highlightRange = selection
|
||||||
|
const editorView = this.editorView
|
||||||
|
const safeEnd = Math.min(
|
||||||
|
selection[1],
|
||||||
|
editorView?.state.doc.length || selection[1]
|
||||||
|
)
|
||||||
|
if (editorView) {
|
||||||
|
editorView.dispatch({
|
||||||
|
effects: addLineHighlight.of([selection[0], safeEnd]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDiagnostics(diagnostics: Diagnostic[]): void {
|
||||||
|
if (!this.editorView) return
|
||||||
|
this.editorView.dispatch(setDiagnostics(this.editorView.state, diagnostics))
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
if (this._editorView) {
|
||||||
|
undo(this._editorView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
if (this._editorView) {
|
||||||
|
redo(this._editorView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set convertToVariableEnabled(enabled: boolean) {
|
||||||
|
this._convertToVariableEnabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set convertToVariableCallback(callback: () => void) {
|
||||||
|
this._convertToVariableCallback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToVariable() {
|
||||||
|
if (this._convertToVariableEnabled) {
|
||||||
|
this._convertToVariableCallback()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
selectRange(selections: Selections) {
|
||||||
|
if (selections.codeBasedSelections.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.editorView) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let codeBasedSelections = []
|
||||||
|
for (const selection of selections.codeBasedSelections) {
|
||||||
|
codeBasedSelections.push(
|
||||||
|
EditorSelection.range(selection.range[0], selection.range[1])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
codeBasedSelections.push(
|
||||||
|
EditorSelection.cursor(
|
||||||
|
selections.codeBasedSelections[
|
||||||
|
selections.codeBasedSelections.length - 1
|
||||||
|
].range[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
this.editorView.dispatch({
|
||||||
|
selection: EditorSelection.create(codeBasedSelections, 1),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnViewUpdate(viewUpdate: ViewUpdate): void {
|
||||||
|
// If we are just fucking around in a snippet, return early and don't
|
||||||
|
// trigger stuff below that might cause the component to re-render.
|
||||||
|
// Otherwise we will not be able to tab thru the snippet portions.
|
||||||
|
// We explicitly dont check HasPrevSnippetField because we always add
|
||||||
|
// a ${} to the end of the function so that's fine.
|
||||||
|
if (hasNextSnippetField(viewUpdate.view.state)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.editorView === null) {
|
||||||
|
this.setEditorView(viewUpdate.view)
|
||||||
|
}
|
||||||
|
const selString = stringifyRanges(
|
||||||
|
viewUpdate?.state?.selection?.ranges || []
|
||||||
|
)
|
||||||
|
|
||||||
|
if (selString === this._lastSelection) {
|
||||||
|
// onUpdate is noisy and is fired a lot by extensions
|
||||||
|
// since we're only interested in selections changes we can ignore most of these.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._lastSelection = selString
|
||||||
|
|
||||||
|
if (
|
||||||
|
this._lastSelectionEvent &&
|
||||||
|
Date.now() - this._lastSelectionEvent < 150
|
||||||
|
) {
|
||||||
|
return // update triggered by scene selection
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sceneInfra.selected) {
|
||||||
|
return // mid drag
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
||||||
|
'Equip Line tool',
|
||||||
|
'Equip tangential arc to',
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!this._modelingEvent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoreEvents.includes(this._modelingEvent.type)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventInfo = processCodeMirrorRanges({
|
||||||
|
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
||||||
|
selectionRanges: this._selectionRanges,
|
||||||
|
isShiftDown: this._isShiftDown,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!eventInfo) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const deterministicEventInfo = {
|
||||||
|
...eventInfo,
|
||||||
|
engineEvents: eventInfo.engineEvents.map((e) => ({
|
||||||
|
...e,
|
||||||
|
cmd_id: 'static',
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
const stringEvent = JSON.stringify(deterministicEventInfo)
|
||||||
|
if (
|
||||||
|
this._lastEvent &&
|
||||||
|
stringEvent === this._lastEvent.event &&
|
||||||
|
Date.now() - this._lastEvent.time < 500
|
||||||
|
) {
|
||||||
|
return // don't repeat events
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lastEvent = { event: stringEvent, time: Date.now() }
|
||||||
|
this._modelingSend(eventInfo.modelingEvent)
|
||||||
|
eventInfo.engineEvents.forEach((event) =>
|
||||||
|
engineCommandManager.sendSceneCommand(event)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyRanges(ranges: readonly SelectionRange[]): string {
|
||||||
|
return ranges.map(({ to, from }) => `${to}->${from}`).join('&')
|
||||||
|
}
|
@ -21,7 +21,7 @@ import { LanguageServerClient } from 'editor/plugins/lsp'
|
|||||||
import { Marked } from '@ts-stack/markdown'
|
import { Marked } from '@ts-stack/markdown'
|
||||||
import { posToOffset } from 'editor/plugins/lsp/util'
|
import { posToOffset } from 'editor/plugins/lsp/util'
|
||||||
import { Program, ProgramMemory } from 'lang/wasm'
|
import { Program, ProgramMemory } from 'lang/wasm'
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, editorManager, kclManager } from 'lib/singletons'
|
||||||
import type { UnitLength } from 'wasm-lib/kcl/bindings/UnitLength'
|
import type { UnitLength } from 'wasm-lib/kcl/bindings/UnitLength'
|
||||||
import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse'
|
import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse'
|
||||||
import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse'
|
import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse'
|
||||||
@ -57,6 +57,9 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
},
|
},
|
||||||
contentChanges: [{ text: code }],
|
contentChanges: [{ text: code }],
|
||||||
})
|
})
|
||||||
|
if (editorManager.editorView) {
|
||||||
|
//editorManager.handleOnViewUpdate(editorManager.editorView)
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
@ -357,15 +360,9 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
try {
|
try {
|
||||||
switch (notification.method) {
|
switch (notification.method) {
|
||||||
case 'textDocument/publishDiagnostics':
|
case 'textDocument/publishDiagnostics':
|
||||||
const params = notification.params as PublishDiagnosticsParams
|
//const params = notification.params as PublishDiagnosticsParams
|
||||||
this.processDiagnostics(params)
|
// this is sometimes slower than our actual typing.
|
||||||
// Update the kcl errors pane.
|
//this.processDiagnostics(params)
|
||||||
/*if (!kclManager.isExecuting) {
|
|
||||||
kclManager.kclErrors = lspDiagnosticsToKclErrors(
|
|
||||||
this.view.state.doc,
|
|
||||||
params.diagnostics
|
|
||||||
)
|
|
||||||
}*/
|
|
||||||
break
|
break
|
||||||
case 'window/logMessage':
|
case 'window/logMessage':
|
||||||
console.log(
|
console.log(
|
||||||
@ -385,17 +382,6 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
// The server has updated the AST, we should update elsewhere.
|
// The server has updated the AST, we should update elsewhere.
|
||||||
let updatedAst = notification.params as Program
|
let updatedAst = notification.params as Program
|
||||||
console.log('[lsp]: Updated AST', updatedAst)
|
console.log('[lsp]: Updated AST', updatedAst)
|
||||||
// Update the ast when we are not already executing.
|
|
||||||
/* if (!kclManager.isExecuting) {
|
|
||||||
kclManager.ast = updatedAst
|
|
||||||
// Execute the ast.
|
|
||||||
console.log('[lsp]: executing ast')
|
|
||||||
await kclManager.executeAst(updatedAst)
|
|
||||||
console.log('[lsp]: executed ast', kclManager.kclErrors)
|
|
||||||
let diagnostics = kclErrorsToDiagnostics(kclManager.kclErrors)
|
|
||||||
this.view.dispatch(setDiagnostics(this.view.state, diagnostics))
|
|
||||||
console.log('[lsp]: updated diagnostics')
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Update the folding ranges, since the AST has changed.
|
// Update the folding ranges, since the AST has changed.
|
||||||
// This is a hack since codemirror does not support async foldService.
|
// This is a hack since codemirror does not support async foldService.
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useStore } from 'useStore'
|
import { editorManager, engineCommandManager } from 'lib/singletons'
|
||||||
import { engineCommandManager } from 'lib/singletons'
|
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
import { getEventForSelectWithPoint } from 'lib/selections'
|
import { getEventForSelectWithPoint } from 'lib/selections'
|
||||||
|
|
||||||
export function useEngineConnectionSubscriptions() {
|
export function useEngineConnectionSubscriptions() {
|
||||||
const { setHighlightRange, highlightRange } = useStore((s) => ({
|
|
||||||
setHighlightRange: s.setHighlightRange,
|
|
||||||
highlightRange: s.highlightRange,
|
|
||||||
}))
|
|
||||||
const { send, context } = useModelingContext()
|
const { send, context } = useModelingContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -21,12 +16,13 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
if (data?.entity_id) {
|
if (data?.entity_id) {
|
||||||
const sourceRange =
|
const sourceRange =
|
||||||
engineCommandManager.artifactMap?.[data.entity_id]?.range
|
engineCommandManager.artifactMap?.[data.entity_id]?.range
|
||||||
setHighlightRange(sourceRange)
|
editorManager.setHighlightRange(sourceRange)
|
||||||
} else if (
|
} else if (
|
||||||
!highlightRange ||
|
!editorManager.highlightRange ||
|
||||||
(highlightRange[0] !== 0 && highlightRange[1] !== 0)
|
(editorManager.highlightRange[0] !== 0 &&
|
||||||
|
editorManager.highlightRange[1] !== 0)
|
||||||
) {
|
) {
|
||||||
setHighlightRange([0, 0])
|
editorManager.setHighlightRange([0, 0])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -43,10 +39,5 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
unSubHover()
|
unSubHover()
|
||||||
unSubClick()
|
unSubClick()
|
||||||
}
|
}
|
||||||
}, [
|
}, [engineCommandManager, context?.sketchEnginePathId])
|
||||||
engineCommandManager,
|
|
||||||
setHighlightRange,
|
|
||||||
highlightRange,
|
|
||||||
context?.sketchEnginePathId,
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useStore } from '../useStore'
|
import { editorManager } from 'lib/singletons'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
// Kurt's note: codeMirror styling overrides were needed to make this work
|
// Kurt's note: codeMirror styling overrides were needed to make this work
|
||||||
@ -6,20 +6,17 @@ import { useEffect } from 'react'
|
|||||||
// search for code-mirror-override in the repo to find the relevant styles
|
// search for code-mirror-override in the repo to find the relevant styles
|
||||||
|
|
||||||
export function useHotKeyListener() {
|
export function useHotKeyListener() {
|
||||||
const { setIsShiftDown } = useStore((s) => ({
|
|
||||||
setIsShiftDown: s.setIsShiftDown,
|
|
||||||
}))
|
|
||||||
const keyName = 'Shift'
|
const keyName = 'Shift'
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) =>
|
const handleKeyDown = (event: KeyboardEvent) =>
|
||||||
event.key === keyName && setIsShiftDown(true)
|
event.key === keyName && editorManager.setIsShiftDown(true)
|
||||||
const handleKeyUp = (event: KeyboardEvent) =>
|
const handleKeyUp = (event: KeyboardEvent) =>
|
||||||
event.key === keyName && setIsShiftDown(false)
|
event.key === keyName && editorManager.setIsShiftDown(false)
|
||||||
window.addEventListener('keydown', handleKeyDown)
|
window.addEventListener('keydown', handleKeyDown)
|
||||||
window.addEventListener('keyup', handleKeyUp)
|
window.addEventListener('keyup', handleKeyUp)
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keydown', handleKeyDown)
|
window.removeEventListener('keydown', handleKeyDown)
|
||||||
window.removeEventListener('keyup', handleKeyUp)
|
window.removeEventListener('keyup', handleKeyUp)
|
||||||
}
|
}
|
||||||
}, [setIsShiftDown])
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import {
|
|||||||
SetVarNameModal,
|
SetVarNameModal,
|
||||||
createSetVarNameModal,
|
createSetVarNameModal,
|
||||||
} from 'components/SetVarNameModal'
|
} from 'components/SetVarNameModal'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { editorManager, kclManager } from 'lib/singletons'
|
||||||
import { moveValueIntoNewVariable } from 'lang/modifyAst'
|
import { moveValueIntoNewVariable } from 'lang/modifyAst'
|
||||||
import { isNodeSafeToReplace } from 'lang/queryAst'
|
import { isNodeSafeToReplace } from 'lang/queryAst'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
@ -13,6 +13,11 @@ const getModalInfo = createSetVarNameModal(SetVarNameModal)
|
|||||||
export function useConvertToVariable() {
|
export function useConvertToVariable() {
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
const [enable, setEnabled] = useState(false)
|
const [enable, setEnabled] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
editorManager.convertToVariableEnabled = enable
|
||||||
|
}, [enable])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { isSafe, value } = isNodeSafeToReplace(
|
const { isSafe, value } = isNodeSafeToReplace(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
@ -45,5 +50,7 @@ export function useConvertToVariable() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editorManager.convertToVariableCallback = handleClick
|
||||||
|
|
||||||
return { enable, handleClick }
|
return { enable, handleClick }
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,10 @@ import { KCLError } from './errors'
|
|||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
import { type IndexLoaderData } from 'lib/types'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
import { useLoaderData } from 'react-router-dom'
|
import { useLoaderData } from 'react-router-dom'
|
||||||
import { useParams } from 'react-router-dom'
|
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
const KclContext = createContext({
|
const KclContext = createContext({
|
||||||
code: codeManager?.code || '',
|
code: codeManager?.code || '',
|
||||||
editorCode: codeManager?.code || '',
|
|
||||||
programMemory: kclManager?.programMemory,
|
programMemory: kclManager?.programMemory,
|
||||||
ast: kclManager?.ast,
|
ast: kclManager?.ast,
|
||||||
isExecuting: kclManager?.isExecuting,
|
isExecuting: kclManager?.isExecuting,
|
||||||
@ -30,7 +28,6 @@ export function KclContextProvider({
|
|||||||
const { code: loadedCode } = useLoaderData() as IndexLoaderData
|
const { code: loadedCode } = useLoaderData() as IndexLoaderData
|
||||||
// Both the code state and the editor state start off with the same code.
|
// Both the code state and the editor state start off with the same code.
|
||||||
const [code, setCode] = useState(loadedCode || codeManager.code)
|
const [code, setCode] = useState(loadedCode || codeManager.code)
|
||||||
const [editorCode, setEditorCode] = useState(code)
|
|
||||||
|
|
||||||
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
||||||
const [ast, setAst] = useState(kclManager.ast)
|
const [ast, setAst] = useState(kclManager.ast)
|
||||||
@ -42,7 +39,6 @@ export function KclContextProvider({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
codeManager.registerCallBacks({
|
codeManager.registerCallBacks({
|
||||||
setCode,
|
setCode,
|
||||||
setEditorCode,
|
|
||||||
})
|
})
|
||||||
kclManager.registerCallBacks({
|
kclManager.registerCallBacks({
|
||||||
setProgramMemory,
|
setProgramMemory,
|
||||||
@ -54,15 +50,10 @@ export function KclContextProvider({
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const params = useParams()
|
|
||||||
useEffect(() => {
|
|
||||||
codeManager.setParams(params)
|
|
||||||
}, [params])
|
|
||||||
return (
|
return (
|
||||||
<KclContext.Provider
|
<KclContext.Provider
|
||||||
value={{
|
value={{
|
||||||
code,
|
code,
|
||||||
editorCode,
|
|
||||||
programMemory,
|
programMemory,
|
||||||
ast,
|
ast,
|
||||||
isExecuting,
|
isExecuting,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { executeAst } from 'useStore'
|
import { executeAst } from 'useStore'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { KCLError } from './errors'
|
import { KCLError, kclErrorsToDiagnostics } from './errors'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { EngineCommandManager } from './std/engineConnection'
|
import { EngineCommandManager } from './std/engineConnection'
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
ExtrudeGroup,
|
ExtrudeGroup,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { getNodeFromPath } from './queryAst'
|
import { getNodeFromPath } from './queryAst'
|
||||||
import { codeManager } from 'lib/singletons'
|
import { codeManager, editorManager } from 'lib/singletons'
|
||||||
|
|
||||||
export class KclManager {
|
export class KclManager {
|
||||||
private _ast: Program = {
|
private _ast: Program = {
|
||||||
@ -43,16 +43,14 @@ export class KclManager {
|
|||||||
const ast = this.safeParse(code)
|
const ast = this.safeParse(code)
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
try {
|
try {
|
||||||
parse(recast(ast)).then((newAst) => {
|
|
||||||
const fmtAndStringify = (ast: Program) =>
|
const fmtAndStringify = (ast: Program) =>
|
||||||
JSON.stringify(newAst)
|
JSON.stringify(parse(recast(ast)))
|
||||||
const isAstTheSame = fmtAndStringify(ast) === fmtAndStringify(this._ast)
|
const isAstTheSame = fmtAndStringify(ast) === fmtAndStringify(this._ast)
|
||||||
if (isAstTheSame) return
|
if (isAstTheSame) return
|
||||||
this.executeAst(ast)
|
|
||||||
})
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
|
this.executeAst(ast)
|
||||||
}, 600)
|
}, 600)
|
||||||
|
|
||||||
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
||||||
@ -92,6 +90,8 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
set kclErrors(kclErrors) {
|
set kclErrors(kclErrors) {
|
||||||
this._kclErrors = kclErrors
|
this._kclErrors = kclErrors
|
||||||
|
let diagnostics = kclErrorsToDiagnostics(kclErrors)
|
||||||
|
editorManager.setDiagnostics(diagnostics)
|
||||||
this._kclErrorsCallBack(kclErrors)
|
this._kclErrorsCallBack(kclErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,9 +145,9 @@ export class KclManager {
|
|||||||
this._executeCallback = callback
|
this._executeCallback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
async safeParse(code: string): Promise<Program | null> {
|
safeParse(code: string): Program | null {
|
||||||
try {
|
try {
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
this.kclErrors = []
|
this.kclErrors = []
|
||||||
return ast
|
return ast
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
import { initPromise, parse } from './wasm'
|
import { initPromise, parse } from './wasm'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('testing AST', () => {
|
describe('testing AST', () => {
|
||||||
test('5 + 6', async () => {
|
test('5 + 6', () => {
|
||||||
const result = await parse('5 +6')
|
const result = parse('5 +6')
|
||||||
delete (result as any).nonCodeMeta
|
delete (result as any).nonCodeMeta
|
||||||
expect(result.body).toEqual([
|
expect(result.body).toEqual([
|
||||||
{
|
{
|
||||||
@ -35,8 +37,8 @@ describe('testing AST', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('const myVar = 5', async () => {
|
test('const myVar = 5', () => {
|
||||||
const { body } = await parse('const myVar = 5')
|
const { body } = parse('const myVar = 5')
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -66,11 +68,11 @@ describe('testing AST', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('multi-line', async () => {
|
test('multi-line', () => {
|
||||||
const code = `const myVar = 5
|
const code = `const myVar = 5
|
||||||
const newVar = myVar + 1
|
const newVar = myVar + 1
|
||||||
`
|
`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -141,8 +143,8 @@ const newVar = myVar + 1
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('testing function declaration', () => {
|
describe('testing function declaration', () => {
|
||||||
test('fn funcN = (a, b) => {return a + b}', async () => {
|
test('fn funcN = (a, b) => {return a + b}', () => {
|
||||||
const { body } = await parse(
|
const { body } = parse(
|
||||||
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
|
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
|
||||||
)
|
)
|
||||||
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
||||||
@ -224,10 +226,10 @@ describe('testing function declaration', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('call expression assignment', async () => {
|
test('call expression assignment', () => {
|
||||||
const code = `fn funcN = (a, b) => { return a + b }
|
const code = `fn funcN = (a, b) => { return a + b }
|
||||||
const myVar = funcN(1, 2)`
|
const myVar = funcN(1, 2)`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
@ -357,14 +359,14 @@ const myVar = funcN(1, 2)`
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('testing pipe operator special', () => {
|
describe('testing pipe operator special', () => {
|
||||||
test('pipe operator with sketch', async () => {
|
test('pipe operator with sketch', () => {
|
||||||
let code = `const mySketch = startSketchAt([0, 0])
|
let code = `const mySketch = startSketchAt([0, 0])
|
||||||
|> lineTo([2, 3], %)
|
|> lineTo([2, 3], %)
|
||||||
|> lineTo([0, 1], %, "myPath")
|
|> lineTo([0, 1], %, "myPath")
|
||||||
|> lineTo([1, 1], %)
|
|> lineTo([1, 1], %)
|
||||||
|> rx(45, %)
|
|> rx(45, %)
|
||||||
`
|
`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
delete (body[0] as any).declarations[0].init.nonCodeMeta
|
delete (body[0] as any).declarations[0].init.nonCodeMeta
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
@ -562,9 +564,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('pipe operator with binary expression', async () => {
|
test('pipe operator with binary expression', () => {
|
||||||
let code = `const myVar = 5 + 6 |> myFunc(45, %)`
|
let code = `const myVar = 5 + 6 |> myFunc(45, %)`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
delete (body as any)[0].declarations[0].init.nonCodeMeta
|
delete (body as any)[0].declarations[0].init.nonCodeMeta
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
@ -641,9 +643,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('array expression', async () => {
|
test('array expression', () => {
|
||||||
let code = `const yo = [1, '2', three, 4 + 5]`
|
let code = `const yo = [1, '2', three, 4 + 5]`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -713,12 +715,12 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object expression ast', async () => {
|
test('object expression ast', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const three = 3',
|
'const three = 3',
|
||||||
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -858,11 +860,11 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('nested object expression ast', async () => {
|
test('nested object expression ast', () => {
|
||||||
const code = `const yo = {key: {
|
const code = `const yo = {key: {
|
||||||
key2: 'value'
|
key2: 'value'
|
||||||
}}`
|
}}`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -928,9 +930,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object expression with array ast', async () => {
|
test('object expression with array ast', () => {
|
||||||
const code = `const yo = {key: [1, '2']}`
|
const code = `const yo = {key: [1, '2']}`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -992,9 +994,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object memberExpression simple', async () => {
|
test('object memberExpression simple', () => {
|
||||||
const code = `const prop = yo.one.two`
|
const code = `const prop = yo.one.two`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -1047,9 +1049,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object memberExpression with square braces', async () => {
|
test('object memberExpression with square braces', () => {
|
||||||
const code = `const prop = yo.one["two"]`
|
const code = `const prop = yo.one["two"]`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -1103,9 +1105,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object memberExpression with two square braces literal and identifier', async () => {
|
test('object memberExpression with two square braces literal and identifier', () => {
|
||||||
const code = `const prop = yo["one"][two]`
|
const code = `const prop = yo["one"][two]`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -1162,9 +1164,9 @@ describe('testing pipe operator special', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('nests binary expressions correctly', () => {
|
describe('nests binary expressions correctly', () => {
|
||||||
it('works with the simple case', async () => {
|
it('works with the simple case', () => {
|
||||||
const code = `const yo = 1 + 2`
|
const code = `const yo = 1 + 2`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body[0]).toEqual({
|
expect(body[0]).toEqual({
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -1205,10 +1207,10 @@ describe('nests binary expressions correctly', () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest according to precedence with multiply first', async () => {
|
it('should nest according to precedence with multiply first', () => {
|
||||||
// should be binExp { binExp { lit-1 * lit-2 } + lit}
|
// should be binExp { binExp { lit-1 * lit-2 } + lit}
|
||||||
const code = `const yo = 1 * 2 + 3`
|
const code = `const yo = 1 * 2 + 3`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body[0]).toEqual({
|
expect(body[0]).toEqual({
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -1262,10 +1264,10 @@ describe('nests binary expressions correctly', () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest according to precedence with sum first', async () => {
|
it('should nest according to precedence with sum first', () => {
|
||||||
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
|
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
|
||||||
const code = `const yo = 1 + 2 * 3`
|
const code = `const yo = 1 + 2 * 3`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect(body[0]).toEqual({
|
expect(body[0]).toEqual({
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -1319,9 +1321,9 @@ describe('nests binary expressions correctly', () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest properly with two operators of equal precedence', async () => {
|
it('should nest properly with two operators of equal precedence', () => {
|
||||||
const code = `const yo = 1 + 2 - 3`
|
const code = `const yo = 1 + 2 - 3`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect((body[0] as any).declarations[0].init).toEqual({
|
expect((body[0] as any).declarations[0].init).toEqual({
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
start: 11,
|
start: 11,
|
||||||
@ -1356,9 +1358,9 @@ describe('nests binary expressions correctly', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest properly with two operators of equal (but higher) precedence', async () => {
|
it('should nest properly with two operators of equal (but higher) precedence', () => {
|
||||||
const code = `const yo = 1 * 2 / 3`
|
const code = `const yo = 1 * 2 / 3`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
expect((body[0] as any).declarations[0].init).toEqual({
|
expect((body[0] as any).declarations[0].init).toEqual({
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
start: 11,
|
start: 11,
|
||||||
@ -1393,9 +1395,9 @@ describe('nests binary expressions correctly', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest properly with longer example', async () => {
|
it('should nest properly with longer example', () => {
|
||||||
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
|
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
const init = (body[0] as any).declarations[0].init
|
const init = (body[0] as any).declarations[0].init
|
||||||
expect(init).toEqual({
|
expect(init).toEqual({
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
@ -1443,7 +1445,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('check nonCodeMeta data is attached to the AST correctly', () => {
|
describe('check nonCodeMeta data is attached to the AST correctly', () => {
|
||||||
it('comments between expressions', async () => {
|
it('comments between expressions', () => {
|
||||||
const code = `
|
const code = `
|
||||||
const yo = { a: { b: { c: '123' } } }
|
const yo = { a: { b: { c: '123' } } }
|
||||||
// this is a comment
|
// this is a comment
|
||||||
@ -1458,14 +1460,12 @@ const key = 'c'`
|
|||||||
value: 'this is a comment',
|
value: 'this is a comment',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const { nonCodeMeta } = await parse(code)
|
const { nonCodeMeta } = parse(code)
|
||||||
expect(nonCodeMeta.nonCodeNodes[0][0]).toEqual(nonCodeMetaInstance)
|
expect(nonCodeMeta.nonCodeNodes[0][0]).toEqual(nonCodeMetaInstance)
|
||||||
|
|
||||||
// extra whitespace won't change it's position (0) or value (NB the start end would have changed though)
|
// extra whitespace won't change it's position (0) or value (NB the start end would have changed though)
|
||||||
const codeWithExtraStartWhitespace = '\n\n\n' + code
|
const codeWithExtraStartWhitespace = '\n\n\n' + code
|
||||||
const { nonCodeMeta: nonCodeMeta2 } = await parse(
|
const { nonCodeMeta: nonCodeMeta2 } = parse(codeWithExtraStartWhitespace)
|
||||||
codeWithExtraStartWhitespace
|
|
||||||
)
|
|
||||||
expect(nonCodeMeta2.nonCodeNodes[0][0].value).toStrictEqual(
|
expect(nonCodeMeta2.nonCodeNodes[0][0].value).toStrictEqual(
|
||||||
nonCodeMetaInstance.value
|
nonCodeMetaInstance.value
|
||||||
)
|
)
|
||||||
@ -1473,7 +1473,7 @@ const key = 'c'`
|
|||||||
nonCodeMetaInstance.start
|
nonCodeMetaInstance.start
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('comments nested within a block statement', async () => {
|
it('comments nested within a block statement', () => {
|
||||||
const code = `const mySketch = startSketchAt([0,0])
|
const code = `const mySketch = startSketchAt([0,0])
|
||||||
|> lineTo([0, 1], %, 'myPath')
|
|> lineTo([0, 1], %, 'myPath')
|
||||||
|> lineTo([1, 1], %) /* this is
|
|> lineTo([1, 1], %) /* this is
|
||||||
@ -1483,7 +1483,7 @@ const key = 'c'`
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
|
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
const indexOfSecondLineToExpression = 2
|
const indexOfSecondLineToExpression = 2
|
||||||
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
|
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
|
||||||
.nonCodeNodes
|
.nonCodeNodes
|
||||||
@ -1498,7 +1498,7 @@ const key = 'c'`
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('comments in a pipe expression', async () => {
|
it('comments in a pipe expression', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const mySk1 = startSketchAt([0, 0])',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' |> lineTo([1, 1], %)',
|
' |> lineTo([1, 1], %)',
|
||||||
@ -1508,7 +1508,7 @@ const key = 'c'`
|
|||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
|
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
|
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
|
||||||
.nonCodeNodes[3][0]
|
.nonCodeNodes[3][0]
|
||||||
expect(sketchNonCodeMeta).toEqual({
|
expect(sketchNonCodeMeta).toEqual({
|
||||||
@ -1525,9 +1525,9 @@ const key = 'c'`
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('test UnaryExpression', () => {
|
describe('test UnaryExpression', () => {
|
||||||
it('should parse a unary expression in simple var dec situation', async () => {
|
it('should parse a unary expression in simple var dec situation', () => {
|
||||||
const code = `const myVar = -min(4, 100)`
|
const code = `const myVar = -min(4, 100)`
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
||||||
expect(myVarInit).toEqual({
|
expect(myVarInit).toEqual({
|
||||||
type: 'UnaryExpression',
|
type: 'UnaryExpression',
|
||||||
@ -1550,9 +1550,9 @@ describe('test UnaryExpression', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('testing nested call expressions', () => {
|
describe('testing nested call expressions', () => {
|
||||||
it('callExp in a binExp in a callExp', async () => {
|
it('callExp in a binExp in a callExp', () => {
|
||||||
const code = 'const myVar = min(100, 1 + legLen(5, 3))'
|
const code = 'const myVar = min(100, 1 + legLen(5, 3))'
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
||||||
expect(myVarInit).toEqual({
|
expect(myVarInit).toEqual({
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
@ -1587,8 +1587,8 @@ describe('testing nested call expressions', () => {
|
|||||||
|
|
||||||
describe('should recognise callExpresions in binaryExpressions', () => {
|
describe('should recognise callExpresions in binaryExpressions', () => {
|
||||||
const code = "xLineTo(segEndX('seg02', %) + 1, %)"
|
const code = "xLineTo(segEndX('seg02', %) + 1, %)"
|
||||||
it('should recognise the callExp', async () => {
|
it('should recognise the callExp', () => {
|
||||||
const { body } = await parse(code)
|
const { body } = parse(code)
|
||||||
const callExpArgs = (body?.[0] as any).expression?.arguments
|
const callExpArgs = (body?.[0] as any).expression?.arguments
|
||||||
expect(callExpArgs).toEqual([
|
expect(callExpArgs).toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { parse, initPromise } from './wasm'
|
import { parse, initPromise } from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('testing artifacts', () => {
|
describe('testing artifacts', () => {
|
||||||
// Enable rotations #152
|
// Enable rotations #152
|
||||||
@ -12,7 +14,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)`
|
// |> rx(45, %)`
|
||||||
const programMemory = await enginelessExecutor(await parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const sketch001 = programMemory?.root?.mySketch001
|
const sketch001 = programMemory?.root?.mySketch001
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
@ -68,7 +70,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> extrude(2, %)`
|
|> extrude(2, %)`
|
||||||
const programMemory = await enginelessExecutor(await parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const sketch001 = programMemory?.root?.mySketch001
|
const sketch001 = programMemory?.root?.mySketch001
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
@ -150,7 +152,7 @@ const sk2 = startSketchOn('XY')
|
|||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
|
|
||||||
`
|
`
|
||||||
const programMemory = await enginelessExecutor(await parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const geos = [programMemory?.root?.theExtrude, programMemory?.root?.sk2]
|
const geos = [programMemory?.root?.theExtrude, programMemory?.root?.sk2]
|
||||||
expect(geos).toEqual([
|
expect(geos).toEqual([
|
||||||
|
@ -5,7 +5,7 @@ import { bracket } from 'lib/exampleKcl'
|
|||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { writeTextFile } from '@tauri-apps/plugin-fs'
|
import { writeTextFile } from '@tauri-apps/plugin-fs'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { Params } from 'react-router-dom'
|
import { editorManager } from 'lib/singletons'
|
||||||
|
|
||||||
const PERSIST_CODE_TOKEN = 'persistCode'
|
const PERSIST_CODE_TOKEN = 'persistCode'
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export default class CodeManager {
|
|||||||
private _code: string = bracket
|
private _code: string = bracket
|
||||||
private _updateState: (arg: string) => void = () => {}
|
private _updateState: (arg: string) => void = () => {}
|
||||||
private _updateEditor: (arg: string) => void = () => {}
|
private _updateEditor: (arg: string) => void = () => {}
|
||||||
private _params: Params<string> = {}
|
private _currentFilePath: string | null = null
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (isTauri()) {
|
if (isTauri()) {
|
||||||
@ -45,19 +45,12 @@ export default class CodeManager {
|
|||||||
return this._code
|
return this._code
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCallBacks({
|
registerCallBacks({ setCode }: { setCode: (arg: string) => void }) {
|
||||||
setCode,
|
|
||||||
setEditorCode,
|
|
||||||
}: {
|
|
||||||
setCode: (arg: string) => void
|
|
||||||
setEditorCode: (arg: string) => void
|
|
||||||
}) {
|
|
||||||
this._updateState = setCode
|
this._updateState = setCode
|
||||||
this._updateEditor = setEditorCode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setParams(params: Params<string>) {
|
updateCurrentFilePath(path: string) {
|
||||||
this._params = params
|
this._currentFilePath = path
|
||||||
}
|
}
|
||||||
|
|
||||||
// This updates the code state and calls the updateState function.
|
// This updates the code state and calls the updateState function.
|
||||||
@ -70,11 +63,14 @@ export default class CodeManager {
|
|||||||
|
|
||||||
// Update the code in the editor.
|
// Update the code in the editor.
|
||||||
updateCodeEditor(code: string): void {
|
updateCodeEditor(code: string): void {
|
||||||
if (this._code !== code) {
|
const lastCode = this._code
|
||||||
this.code = code
|
this.code = code
|
||||||
this._updateEditor(code)
|
this._updateEditor(code)
|
||||||
|
if (editorManager.editorView) {
|
||||||
|
editorManager.editorView.dispatch({
|
||||||
|
changes: { from: 0, to: lastCode.length, insert: code },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
this._updateEditor(code)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the code, state, and the code the code mirror editor sees.
|
// Update the code, state, and the code the code mirror editor sees.
|
||||||
@ -91,8 +87,8 @@ export default class CodeManager {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Wait one event loop to give a chance for params to be set
|
// Wait one event loop to give a chance for params to be set
|
||||||
// Save the file to disk
|
// Save the file to disk
|
||||||
this._params.id &&
|
this._currentFilePath &&
|
||||||
writeTextFile(this._params.id, this.code).catch((err) => {
|
writeTextFile(this._currentFilePath, this.code).catch((err) => {
|
||||||
// TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
// TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
||||||
console.error('error saving file', err)
|
console.error('error saving file', err)
|
||||||
toast.error('Error saving file, please check file permissions')
|
toast.error('Error saving file, please check file permissions')
|
||||||
|
@ -4,7 +4,9 @@ import { parse, ProgramMemory, SketchGroup, initPromise } from './wasm'
|
|||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('test executor', () => {
|
describe('test executor', () => {
|
||||||
it('test assigning two variables, the second summing with the first', async () => {
|
it('test assigning two variables, the second summing with the first', async () => {
|
||||||
@ -398,7 +400,7 @@ async function exe(
|
|||||||
code: string,
|
code: string,
|
||||||
programMemory: ProgramMemory = { root: {}, return: null }
|
programMemory: ProgramMemory = { root: {}, return: null }
|
||||||
) {
|
) {
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
|
|
||||||
const result = await enginelessExecutor(ast, programMemory)
|
const result = await enginelessExecutor(ast, programMemory)
|
||||||
return result
|
return result
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
||||||
import { Identifier, parse, initPromise, Parameter } from './wasm'
|
import { Identifier, parse, initPromise, Parameter } from './wasm'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('testing getNodePathFromSourceRange', () => {
|
describe('testing getNodePathFromSourceRange', () => {
|
||||||
it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', async () => {
|
it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', () => {
|
||||||
const code = `
|
const code = `
|
||||||
const myVar = 5
|
const myVar = 5
|
||||||
const sk3 = startSketchAt([0, 0])
|
const sk3 = startSketchAt([0, 0])
|
||||||
@ -19,14 +21,14 @@ const sk3 = startSketchAt([0, 0])
|
|||||||
lineToSubstringIndex + subStr.length,
|
lineToSubstringIndex + subStr.length,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const { node } = getNodeFromPath<any>(ast, nodePath)
|
const { node } = getNodeFromPath<any>(ast, nodePath)
|
||||||
|
|
||||||
expect([node.start, node.end]).toEqual(sourceRange)
|
expect([node.start, node.end]).toEqual(sourceRange)
|
||||||
expect(node.type).toBe('CallExpression')
|
expect(node.type).toBe('CallExpression')
|
||||||
})
|
})
|
||||||
it('gets path right for function definition params', async () => {
|
it('gets path right for function definition params', () => {
|
||||||
const code = `fn cube = (pos, scale) => {
|
const code = `fn cube = (pos, scale) => {
|
||||||
const sg = startSketchAt(pos)
|
const sg = startSketchAt(pos)
|
||||||
|> line([0, scale], %)
|
|> line([0, scale], %)
|
||||||
@ -44,7 +46,7 @@ const b1 = cube([0,0], 10)`
|
|||||||
subStrIndex + 'pos'.length,
|
subStrIndex + 'pos'.length,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const node = getNodeFromPath<Parameter>(ast, nodePath).node
|
const node = getNodeFromPath<Parameter>(ast, nodePath).node
|
||||||
|
|
||||||
@ -60,7 +62,7 @@ const b1 = cube([0,0], 10)`
|
|||||||
expect(node.type).toBe('Parameter')
|
expect(node.type).toBe('Parameter')
|
||||||
expect(node.identifier.name).toBe('pos')
|
expect(node.identifier.name).toBe('pos')
|
||||||
})
|
})
|
||||||
it('gets path right for deep within function definition body', async () => {
|
it('gets path right for deep within function definition body', () => {
|
||||||
const code = `fn cube = (pos, scale) => {
|
const code = `fn cube = (pos, scale) => {
|
||||||
const sg = startSketchAt(pos)
|
const sg = startSketchAt(pos)
|
||||||
|> line([0, scale], %)
|
|> line([0, scale], %)
|
||||||
@ -78,7 +80,7 @@ const b1 = cube([0,0], 10)`
|
|||||||
subStrIndex + 'scale'.length,
|
subStrIndex + 'scale'.length,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const node = getNodeFromPath<Identifier>(ast, nodePath).node
|
const node = getNodeFromPath<Identifier>(ast, nodePath).node
|
||||||
expect(nodePath).toEqual([
|
expect(nodePath).toEqual([
|
||||||
|
@ -17,7 +17,9 @@ import {
|
|||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { getNodePathFromSourceRange } from './queryAst'
|
import { getNodePathFromSourceRange } from './queryAst'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('Testing createLiteral', () => {
|
describe('Testing createLiteral', () => {
|
||||||
it('should create a literal', () => {
|
it('should create a literal', () => {
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
import {
|
|
||||||
ParserWorkerResponse,
|
|
||||||
WasmWorker,
|
|
||||||
WasmWorkerEventType,
|
|
||||||
ParserWorkerCallResponse,
|
|
||||||
} from 'lang/workers/types'
|
|
||||||
import Worker from 'lang/workers/parser?worker'
|
|
||||||
import { Program, wasmUrl } from 'lang/wasm'
|
|
||||||
import { KCLError } from 'lang/errors'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
|
|
||||||
export default class Parser {
|
|
||||||
worker: any = new Worker({ name: 'parse' })
|
|
||||||
intitalized: boolean = false
|
|
||||||
mappings: Map<string, Program | KCLError> = new Map()
|
|
||||||
|
|
||||||
async parse(code: string): Promise<Program> {
|
|
||||||
this.ensureWorker()
|
|
||||||
const uuid = uuidv4()
|
|
||||||
this.worker.postMessage({
|
|
||||||
worker: WasmWorker.Parser,
|
|
||||||
eventType: WasmWorkerEventType.Call,
|
|
||||||
eventData: {
|
|
||||||
uuid,
|
|
||||||
code,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
let result = await this.waitForResult(uuid)
|
|
||||||
if (result instanceof KCLError) {
|
|
||||||
throw result
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForResult(uuid: string): Promise<Program | KCLError> {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const result = this.mappings.get(uuid)
|
|
||||||
if (result) {
|
|
||||||
this.mappings.delete(uuid)
|
|
||||||
resolve(result)
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(this.waitForResult(uuid))
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureWorker() {
|
|
||||||
if (!this.intitalized) {
|
|
||||||
this.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the worker.
|
|
||||||
start() {
|
|
||||||
if (this.intitalized) {
|
|
||||||
console.log('Worker already initialized')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.worker.postMessage({
|
|
||||||
worker: WasmWorker.Parser,
|
|
||||||
eventType: WasmWorkerEventType.Init,
|
|
||||||
eventData: {
|
|
||||||
wasmUrl: wasmUrl(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
this.worker.onmessage = function (r: ParserWorkerResponse) {
|
|
||||||
switch (r.eventType) {
|
|
||||||
case WasmWorkerEventType.Init:
|
|
||||||
this.intitalized = true
|
|
||||||
break
|
|
||||||
case WasmWorkerEventType.Call:
|
|
||||||
const c = r.response as ParserWorkerCallResponse
|
|
||||||
this.mappings.set(c.uuid, c.response)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,7 +15,9 @@ import {
|
|||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
} from './modifyAst'
|
} from './modifyAst'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('findAllPreviousVariables', () => {
|
describe('findAllPreviousVariables', () => {
|
||||||
it('should find all previous variables', async () => {
|
it('should find all previous variables', async () => {
|
||||||
|
@ -1,56 +1,58 @@
|
|||||||
import { parse, Program, recast, initPromise } from './wasm'
|
import { parse, Program, recast, initPromise } from './wasm'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('recast', () => {
|
describe('recast', () => {
|
||||||
it('recasts a simple program', async () => {
|
it('recasts a simple program', () => {
|
||||||
const code = '1 + 2'
|
const code = '1 + 2'
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('variable declaration', async () => {
|
it('variable declaration', () => {
|
||||||
const code = 'const myVar = 5'
|
const code = 'const myVar = 5'
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it("variable declaration that's binary with string", async () => {
|
it("variable declaration that's binary with string", () => {
|
||||||
const code = "const myVar = 5 + 'yo'"
|
const code = "const myVar = 5 + 'yo'"
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
const codeWithOtherQuotes = 'const myVar = 5 + "yo"'
|
const codeWithOtherQuotes = 'const myVar = 5 + "yo"'
|
||||||
const { ast: ast2 } = await code2ast(codeWithOtherQuotes)
|
const { ast: ast2 } = code2ast(codeWithOtherQuotes)
|
||||||
expect(recast(ast2).trim()).toBe(codeWithOtherQuotes)
|
expect(recast(ast2).trim()).toBe(codeWithOtherQuotes)
|
||||||
})
|
})
|
||||||
it('test assigning two variables, the second summing with the first', async () => {
|
it('test assigning two variables, the second summing with the first', () => {
|
||||||
const code = `const myVar = 5
|
const code = `const myVar = 5
|
||||||
const newVar = myVar + 1
|
const newVar = myVar + 1
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('test assigning a var by cont concatenating two strings string', async () => {
|
it('test assigning a var by cont concatenating two strings string', () => {
|
||||||
const code = fs.readFileSync(
|
const code = fs.readFileSync(
|
||||||
'./src/lang/testExamples/variableDeclaration.cado',
|
'./src/lang/testExamples/variableDeclaration.cado',
|
||||||
'utf-8'
|
'utf-8'
|
||||||
)
|
)
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('test with function call', async () => {
|
it('test with function call', () => {
|
||||||
const code = `const myVar = "hello"
|
const code = `const myVar = "hello"
|
||||||
log(5, myVar)
|
log(5, myVar)
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('function declaration with call', async () => {
|
it('function declaration with call', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'fn funcN = (a, b) => {',
|
'fn funcN = (a, b) => {',
|
||||||
' return a + b',
|
' return a + b',
|
||||||
@ -58,22 +60,22 @@ log(5, myVar)
|
|||||||
'const theVar = 60',
|
'const theVar = 60',
|
||||||
'const magicNum = funcN(9, theVar)',
|
'const magicNum = funcN(9, theVar)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('recast sketch declaration', async () => {
|
it('recast sketch declaration', () => {
|
||||||
let code = `const mySketch = startSketchAt([0, 0])
|
let code = `const mySketch = startSketchAt([0, 0])
|
||||||
|> lineTo([0, 1], %, "myPath")
|
|> lineTo([0, 1], %, "myPath")
|
||||||
|> lineTo([1, 1], %)
|
|> lineTo([1, 1], %)
|
||||||
|> lineTo([1, 0], %, "rightPath")
|
|> lineTo([1, 0], %, "rightPath")
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('sketch piped into callExpression', async () => {
|
it('sketch piped into callExpression', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const mySk1 = startSketchAt([0, 0])',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' |> lineTo([1, 1], %)',
|
' |> lineTo([1, 1], %)',
|
||||||
@ -81,11 +83,11 @@ log(5, myVar)
|
|||||||
' |> lineTo([1, 1], %)',
|
' |> lineTo([1, 1], %)',
|
||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast BinaryExpression piped into CallExpression', async () => {
|
it('recast BinaryExpression piped into CallExpression', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'fn myFn = (a) => {',
|
'fn myFn = (a) => {',
|
||||||
' return a + 1',
|
' return a + 1',
|
||||||
@ -93,49 +95,49 @@ log(5, myVar)
|
|||||||
'const myVar = 5 + 1',
|
'const myVar = 5 + 1',
|
||||||
' |> myFn(%)',
|
' |> myFn(%)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('recast nested binary expression', async () => {
|
it('recast nested binary expression', () => {
|
||||||
const code = ['const myVar = 1 + 2 * 5'].join('\n')
|
const code = ['const myVar = 1 + 2 * 5'].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast nested binary expression with parans', async () => {
|
it('recast nested binary expression with parans', () => {
|
||||||
const code = ['const myVar = 1 + (1 + 2) * 5'].join('\n')
|
const code = ['const myVar = 1 + (1 + 2) * 5'].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('unnecessary paran wrap will be remove', async () => {
|
it('unnecessary paran wrap will be remove', () => {
|
||||||
const code = ['const myVar = 1 + (2 * 5)'].join('\n')
|
const code = ['const myVar = 1 + (2 * 5)'].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.replace('(', '').replace(')', ''))
|
expect(recasted.trim()).toBe(code.replace('(', '').replace(')', ''))
|
||||||
})
|
})
|
||||||
it('complex nested binary expression', async () => {
|
it('complex nested binary expression', () => {
|
||||||
const code = ['1 * ((2 + 3) / 4 + 5)'].join('\n')
|
const code = ['1 * ((2 + 3) / 4 + 5)'].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('multiplied paren expressions', async () => {
|
it('multiplied paren expressions', () => {
|
||||||
const code = ['3 + (1 + 2) * (3 + 4)'].join('\n')
|
const code = ['3 + (1 + 2) * (3 + 4)'].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast array declaration', async () => {
|
it('recast array declaration', () => {
|
||||||
const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
|
const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
|
||||||
'\n'
|
'\n'
|
||||||
)
|
)
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast long array declaration', async () => {
|
it('recast long array declaration', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const three = 3',
|
'const three = 3',
|
||||||
'const yo = [',
|
'const yo = [',
|
||||||
@ -146,11 +148,11 @@ log(5, myVar)
|
|||||||
" 'hey oooooo really long long long'",
|
" 'hey oooooo really long long long'",
|
||||||
']',
|
']',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast long object execution', async () => {
|
it('recast long object execution', () => {
|
||||||
const code = `const three = 3
|
const code = `const three = 3
|
||||||
const yo = {
|
const yo = {
|
||||||
aStr: 'str',
|
aStr: 'str',
|
||||||
@ -159,43 +161,43 @@ const yo = {
|
|||||||
binExp: 4 + 5
|
binExp: 4 + 5
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('recast short object execution', async () => {
|
it('recast short object execution', () => {
|
||||||
const code = `const yo = { key: 'val' }
|
const code = `const yo = { key: 'val' }
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('recast object execution with member expression', async () => {
|
it('recast object execution with member expression', () => {
|
||||||
const code = `const yo = { a: { b: { c: '123' } } }
|
const code = `const yo = { a: { b: { c: '123' } } }
|
||||||
const key = 'c'
|
const key = 'c'
|
||||||
const myVar = yo.a['b'][key]
|
const myVar = yo.a['b'][key]
|
||||||
const key2 = 'b'
|
const key2 = 'b'
|
||||||
const myVar2 = yo['a'][key2].c
|
const myVar2 = yo['a'][key2].c
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('testing recasting with comments and whitespace', () => {
|
describe('testing recasting with comments and whitespace', () => {
|
||||||
it('code with comments', async () => {
|
it('code with comments', () => {
|
||||||
const code = `const yo = { a: { b: { c: '123' } } }
|
const code = `const yo = { a: { b: { c: '123' } } }
|
||||||
// this is a comment
|
// this is a comment
|
||||||
const key = 'c'
|
const key = 'c'
|
||||||
`
|
`
|
||||||
|
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
|
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('code with comment and extra lines', async () => {
|
it('code with comment and extra lines', () => {
|
||||||
const code = `const yo = 'c'
|
const code = `const yo = 'c'
|
||||||
|
|
||||||
/* this is
|
/* this is
|
||||||
@ -203,23 +205,23 @@ a
|
|||||||
comment */
|
comment */
|
||||||
const yo = 'bing'
|
const yo = 'bing'
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('comments at the start and end', async () => {
|
it('comments at the start and end', () => {
|
||||||
const code = `// this is a comment
|
const code = `// this is a comment
|
||||||
const yo = { a: { b: { c: '123' } } }
|
const yo = { a: { b: { c: '123' } } }
|
||||||
const key = 'c'
|
const key = 'c'
|
||||||
|
|
||||||
// this is also a comment
|
// this is also a comment
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('comments in a fn block', async () => {
|
it('comments in a fn block', () => {
|
||||||
const code = `fn myFn = async () => {
|
const code = `fn myFn = () => {
|
||||||
// this is a comment
|
// this is a comment
|
||||||
const yo = { a: { b: { c: '123' } } }
|
const yo = { a: { b: { c: '123' } } }
|
||||||
|
|
||||||
@ -229,11 +231,11 @@ const key = 'c'
|
|||||||
// this is also a comment
|
// this is also a comment
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('comments in a pipe expression', async () => {
|
it('comments in a pipe expression', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const mySk1 = startSketchAt([0, 0])',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' |> lineTo([1, 1], %)',
|
' |> lineTo([1, 1], %)',
|
||||||
@ -242,11 +244,11 @@ const key = 'c'
|
|||||||
' // a comment',
|
' // a comment',
|
||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('comments sprinkled in all over the place', async () => {
|
it('comments sprinkled in all over the place', () => {
|
||||||
const code = `
|
const code = `
|
||||||
/* comment at start */
|
/* comment at start */
|
||||||
|
|
||||||
@ -268,7 +270,7 @@ const mySk1 = startSketchAt([0, 0])
|
|||||||
one more for good measure
|
one more for good measure
|
||||||
*/
|
*/
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(`/* comment at start */
|
expect(recasted).toBe(`/* comment at start */
|
||||||
|
|
||||||
@ -289,37 +291,37 @@ const mySk1 = startSketchAt([0, 0])
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('testing call Expressions in BinaryExpressions and UnaryExpressions', () => {
|
describe('testing call Expressions in BinaryExpressions and UnaryExpressions', () => {
|
||||||
it('nested callExpression in binaryExpression', async () => {
|
it('nested callExpression in binaryExpression', () => {
|
||||||
const code = 'const myVar = 2 + min(100, legLen(5, 3))'
|
const code = 'const myVar = 2 + min(100, legLen(5, 3))'
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('nested callExpression in unaryExpression', async () => {
|
it('nested callExpression in unaryExpression', () => {
|
||||||
const code = 'const myVar = -min(100, legLen(5, 3))'
|
const code = 'const myVar = -min(100, legLen(5, 3))'
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('with unaryExpression in callExpression', async () => {
|
it('with unaryExpression in callExpression', () => {
|
||||||
const code = 'const myVar = min(5, -legLen(5, 4))'
|
const code = 'const myVar = min(5, -legLen(5, 4))'
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('with unaryExpression in sketch situation', async () => {
|
it('with unaryExpression in sketch situation', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const part001 = startSketchAt([0, 0])',
|
'const part001 = startSketchAt([0, 0])',
|
||||||
' |> line([-2.21, -legLen(5, min(3, 999))], %)',
|
' |> line([-2.21, -legLen(5, min(3, 999))], %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('it recasts wrapped object expressions in pipe bodies with correct indentation', () => {
|
describe('it recasts wrapped object expressions in pipe bodies with correct indentation', () => {
|
||||||
it('with a single line', async () => {
|
it('with a single line', () => {
|
||||||
const code = `const part001 = startSketchAt([-0.01, -0.08])
|
const code = `const part001 = startSketchAt([-0.01, -0.08])
|
||||||
|> line([0.62, 4.15], %, 'seg01')
|
|> line([0.62, 4.15], %, 'seg01')
|
||||||
|> line([2.77, -1.24], %)
|
|> line([2.77, -1.24], %)
|
||||||
@ -330,35 +332,35 @@ describe('it recasts wrapped object expressions in pipe bodies with correct inde
|
|||||||
}, %)
|
}, %)
|
||||||
|> line([-0.42, -1.72], %)
|
|> line([-0.42, -1.72], %)
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('recasts wrapped object expressions NOT in pipe body correctly', async () => {
|
it('recasts wrapped object expressions NOT in pipe body correctly', () => {
|
||||||
const code = `angledLineThatIntersects({
|
const code = `angledLineThatIntersects({
|
||||||
angle: 201,
|
angle: 201,
|
||||||
offset: -1.35,
|
offset: -1.35,
|
||||||
intersectTag: 'seg01'
|
intersectTag: 'seg01'
|
||||||
}, %)
|
}, %)
|
||||||
`
|
`
|
||||||
const { ast } = await code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('it recasts binary expression using brackets where needed', () => {
|
describe('it recasts binary expression using brackets where needed', () => {
|
||||||
it('when there are two minus in a row', async () => {
|
it('when there are two minus in a row', () => {
|
||||||
const code = `const part001 = 1 - (def - abc)
|
const code = `const part001 = 1 - (def - abc)
|
||||||
`
|
`
|
||||||
const recasted = recast((await code2ast(code)).ast)
|
const recasted = recast(code2ast(code).ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
|
||||||
async function code2ast(code: string): Promise<{ ast: Program }> {
|
function code2ast(code: string): { ast: Program } {
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
return { ast }
|
return { ast }
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,9 @@ const eachQuad: [number, [number, number]][] = [
|
|||||||
[675, [1, -1]],
|
[675, [1, -1]],
|
||||||
]
|
]
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('testing getYComponent', () => {
|
describe('testing getYComponent', () => {
|
||||||
it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
|
it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
|
||||||
@ -104,7 +106,7 @@ describe('testing changeSketchArguments', () => {
|
|||||||
`
|
`
|
||||||
const code = genCode(lineToChange)
|
const code = genCode(lineToChange)
|
||||||
const expectedCode = genCode(lineAfterChange)
|
const expectedCode = genCode(lineAfterChange)
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(lineToChange)
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
const { modifiedAst } = changeSketchArguments(
|
const { modifiedAst } = changeSketchArguments(
|
||||||
@ -128,7 +130,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)`
|
|> lineTo([0.46, -5.82], %)`
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(lineToChange)
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
expect(sourceStart).toBe(95)
|
expect(sourceStart).toBe(95)
|
||||||
@ -190,7 +192,7 @@ describe('testing addTagForSketchOnFace', () => {
|
|||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
`
|
`
|
||||||
const code = genCode(originalLine)
|
const code = genCode(originalLine)
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(originalLine)
|
const sourceStart = code.indexOf(originalLine)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number] = [
|
||||||
|
@ -8,7 +8,9 @@ import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
|||||||
import { Selection } from 'lib/selections'
|
import { Selection } from 'lib/selections'
|
||||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
// testing helper function
|
// testing helper function
|
||||||
async function testingSwapSketchFnCall({
|
async function testingSwapSketchFnCall({
|
||||||
@ -28,7 +30,7 @@ async function testingSwapSketchFnCall({
|
|||||||
type: 'default',
|
type: 'default',
|
||||||
range: [startIndex, startIndex + callToSwap.length],
|
range: [startIndex, startIndex + callToSwap.length],
|
||||||
}
|
}
|
||||||
const ast = await parse(inputCode)
|
const ast = parse(inputCode)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const selections = {
|
const selections = {
|
||||||
codeBasedSelections: [range],
|
codeBasedSelections: [range],
|
||||||
@ -351,7 +353,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> line([2.14, 1.35], %) // normal-segment
|
|> line([2.14, 1.35], %) // normal-segment
|
||||||
|> xLine(3.54, %)`
|
|> xLine(3.54, %)`
|
||||||
it('normal case works', async () => {
|
it('normal case works', async () => {
|
||||||
const programMemory = await enginelessExecutor(await parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
const index = code.indexOf('// normal-segment') - 7
|
const index = code.indexOf('// normal-segment') - 7
|
||||||
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
||||||
programMemory.root['part001'] as SketchGroup,
|
programMemory.root['part001'] as SketchGroup,
|
||||||
@ -365,7 +367,7 @@ const part001 = startSketchOn('XY')
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('verify it works when the segment is in the `start` property', async () => {
|
it('verify it works when the segment is in the `start` property', async () => {
|
||||||
const programMemory = await enginelessExecutor(await parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
const index = code.indexOf('// segment-in-start') - 7
|
const index = code.indexOf('// segment-in-start') - 7
|
||||||
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
||||||
programMemory.root['part001'] as SketchGroup,
|
programMemory.root['part001'] as SketchGroup,
|
||||||
|
@ -11,57 +11,59 @@ import { ToolTip } from '../../useStore'
|
|||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('testing getConstraintType', () => {
|
describe('testing getConstraintType', () => {
|
||||||
const helper = getConstraintTypeFromSourceHelper
|
const helper = getConstraintTypeFromSourceHelper
|
||||||
it('testing line', async () => {
|
it('testing line', () => {
|
||||||
expect(await helper(`line([5, myVar], %)`)).toBe('yRelative')
|
expect(helper(`line([5, myVar], %)`)).toBe('yRelative')
|
||||||
expect(await helper(`line([myVar, 5], %)`)).toBe('xRelative')
|
expect(helper(`line([myVar, 5], %)`)).toBe('xRelative')
|
||||||
})
|
})
|
||||||
it('testing lineTo', async () => {
|
it('testing lineTo', () => {
|
||||||
expect(await helper(`lineTo([5, myVar], %)`)).toBe('yAbsolute')
|
expect(helper(`lineTo([5, myVar], %)`)).toBe('yAbsolute')
|
||||||
expect(await helper(`lineTo([myVar, 5], %)`)).toBe('xAbsolute')
|
expect(helper(`lineTo([myVar, 5], %)`)).toBe('xAbsolute')
|
||||||
})
|
})
|
||||||
it('testing angledLine', async () => {
|
it('testing angledLine', () => {
|
||||||
expect(await helper(`angledLine([5, myVar], %)`)).toBe('length')
|
expect(helper(`angledLine([5, myVar], %)`)).toBe('length')
|
||||||
expect(await helper(`angledLine([myVar, 5], %)`)).toBe('angle')
|
expect(helper(`angledLine([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
it('testing angledLineOfXLength', async () => {
|
it('testing angledLineOfXLength', () => {
|
||||||
expect(await helper(`angledLineOfXLength([5, myVar], %)`)).toBe('xRelative')
|
expect(helper(`angledLineOfXLength([5, myVar], %)`)).toBe('xRelative')
|
||||||
expect(await helper(`angledLineOfXLength([myVar, 5], %)`)).toBe('angle')
|
expect(helper(`angledLineOfXLength([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
it('testing angledLineToX', async () => {
|
it('testing angledLineToX', () => {
|
||||||
expect(await helper(`angledLineToX([5, myVar], %)`)).toBe('xAbsolute')
|
expect(helper(`angledLineToX([5, myVar], %)`)).toBe('xAbsolute')
|
||||||
expect(await helper(`angledLineToX([myVar, 5], %)`)).toBe('angle')
|
expect(helper(`angledLineToX([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
it('testing angledLineOfYLength', async () => {
|
it('testing angledLineOfYLength', () => {
|
||||||
expect(await helper(`angledLineOfYLength([5, myVar], %)`)).toBe('yRelative')
|
expect(helper(`angledLineOfYLength([5, myVar], %)`)).toBe('yRelative')
|
||||||
expect(await helper(`angledLineOfYLength([myVar, 5], %)`)).toBe('angle')
|
expect(helper(`angledLineOfYLength([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
it('testing angledLineToY', async () => {
|
it('testing angledLineToY', () => {
|
||||||
expect(await helper(`angledLineToY([5, myVar], %)`)).toBe('yAbsolute')
|
expect(helper(`angledLineToY([5, myVar], %)`)).toBe('yAbsolute')
|
||||||
expect(await helper(`angledLineToY([myVar, 5], %)`)).toBe('angle')
|
expect(helper(`angledLineToY([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
const helper2 = getConstraintTypeFromSourceHelper2
|
const helper2 = getConstraintTypeFromSourceHelper2
|
||||||
it('testing xLine', async () => {
|
it('testing xLine', () => {
|
||||||
expect(await helper2(`xLine(5, %)`)).toBe('yRelative')
|
expect(helper2(`xLine(5, %)`)).toBe('yRelative')
|
||||||
})
|
})
|
||||||
it('testing yLine', async () => {
|
it('testing yLine', () => {
|
||||||
expect(await helper2(`yLine(5, %)`)).toBe('xRelative')
|
expect(helper2(`yLine(5, %)`)).toBe('xRelative')
|
||||||
})
|
})
|
||||||
it('testing xLineTo', async () => {
|
it('testing xLineTo', () => {
|
||||||
expect(await helper2(`xLineTo(5, %)`)).toBe('yAbsolute')
|
expect(helper2(`xLineTo(5, %)`)).toBe('yAbsolute')
|
||||||
})
|
})
|
||||||
it('testing yLineTo', async () => {
|
it('testing yLineTo', () => {
|
||||||
expect(await helper2(`yLineTo(5, %)`)).toBe('xAbsolute')
|
expect(helper2(`yLineTo(5, %)`)).toBe('xAbsolute')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getConstraintTypeFromSourceHelper(
|
function getConstraintTypeFromSourceHelper(
|
||||||
code: string
|
code: string
|
||||||
): Promise<ReturnType<typeof getConstraintType>> {
|
): ReturnType<typeof getConstraintType> {
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const args = (ast.body[0] as any).expression.arguments[0].elements as [
|
const args = (ast.body[0] as any).expression.arguments[0].elements as [
|
||||||
Value,
|
Value,
|
||||||
Value
|
Value
|
||||||
@ -69,10 +71,10 @@ async function getConstraintTypeFromSourceHelper(
|
|||||||
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
||||||
return getConstraintType(args, fnName)
|
return getConstraintType(args, fnName)
|
||||||
}
|
}
|
||||||
async function getConstraintTypeFromSourceHelper2(
|
function getConstraintTypeFromSourceHelper2(
|
||||||
code: string
|
code: string
|
||||||
): Promise<ReturnType<typeof getConstraintType>> {
|
): ReturnType<typeof getConstraintType> {
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const arg = (ast.body[0] as any).expression.arguments[0] as Value
|
const arg = (ast.body[0] as any).expression.arguments[0] as Value
|
||||||
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
||||||
return getConstraintType(arg, fnName)
|
return getConstraintType(arg, fnName)
|
||||||
@ -197,7 +199,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine
|
|> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine
|
||||||
`
|
`
|
||||||
it('should transform the ast', async () => {
|
it('should transform the ast', async () => {
|
||||||
const ast = await parse(inputScript)
|
const ast = parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('//'))
|
.filter((ln) => ln.includes('//'))
|
||||||
@ -284,7 +286,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
||||||
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
||||||
`
|
`
|
||||||
const ast = await parse(inputScript)
|
const ast = parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('// select for horizontal constraint'))
|
.filter((ln) => ln.includes('// select for horizontal constraint'))
|
||||||
@ -342,7 +344,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
||||||
|> yLineTo(myVar, %) // select for vertical constraint 10
|
|> yLineTo(myVar, %) // select for vertical constraint 10
|
||||||
`
|
`
|
||||||
const ast = await parse(inputScript)
|
const ast = parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('// select for vertical constraint'))
|
.filter((ln) => ln.includes('// select for vertical constraint'))
|
||||||
@ -433,7 +435,7 @@ async function helperThing(
|
|||||||
linesOfInterest: string[],
|
linesOfInterest: string[],
|
||||||
constraint: ConstraintType
|
constraint: ConstraintType
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const ast = await parse(inputScript)
|
const ast = parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) =>
|
.filter((ln) =>
|
||||||
@ -465,7 +467,7 @@ async function helperThing(
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('testing getConstraintLevelFromSourceRange', () => {
|
describe('testing getConstraintLevelFromSourceRange', () => {
|
||||||
it('should divide up lines into free, partial and fully contrained', async () => {
|
it('should divide up lines into free, partial and fully contrained', () => {
|
||||||
const code = `const baseLength = 3
|
const code = `const baseLength = 3
|
||||||
const baseThick = 1
|
const baseThick = 1
|
||||||
const armThick = 0.5
|
const armThick = 0.5
|
||||||
@ -495,7 +497,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> line([-1.49, 1.06], %) // free
|
|> line([-1.49, 1.06], %) // free
|
||||||
|> xLine(-3.43 + 0, %) // full
|
|> xLine(-3.43 + 0, %) // full
|
||||||
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full`
|
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full`
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const constraintLevels: ReturnType<
|
const constraintLevels: ReturnType<
|
||||||
typeof getConstraintLevelFromSourceRange
|
typeof getConstraintLevelFromSourceRange
|
||||||
>['level'][] = ['full', 'partial', 'free']
|
>['level'][] = ['full', 'partial', 'free']
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { parse, initPromise } from '../wasm'
|
import { parse, initPromise } from '../wasm'
|
||||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('testing angledLineThatIntersects', () => {
|
describe('testing angledLineThatIntersects', () => {
|
||||||
it('angledLineThatIntersects should intersect with another line', async () => {
|
it('angledLineThatIntersects should intersect with another line', async () => {
|
||||||
@ -15,9 +17,9 @@ describe('testing angledLineThatIntersects', () => {
|
|||||||
offset: ${offset},
|
offset: ${offset},
|
||||||
}, %, "yo2")
|
}, %, "yo2")
|
||||||
const intersect = segEndX('yo2', part001)`
|
const intersect = segEndX('yo2', part001)`
|
||||||
const { root } = await enginelessExecutor(await parse(code('-1')))
|
const { root } = await enginelessExecutor(parse(code('-1')))
|
||||||
expect(root.intersect.value).toBe(1 + Math.sqrt(2))
|
expect(root.intersect.value).toBe(1 + Math.sqrt(2))
|
||||||
const { root: noOffset } = await enginelessExecutor(await parse(code('0')))
|
const { root: noOffset } = await enginelessExecutor(parse(code('0')))
|
||||||
expect(noOffset.intersect.value).toBeCloseTo(1)
|
expect(noOffset.intersect.value).toBeCloseTo(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { lexer, initPromise } from './wasm'
|
import { lexer, initPromise } from './wasm'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('testing lexer', () => {
|
describe('testing lexer', () => {
|
||||||
it('async lexer works too', async () => {
|
it('async lexer works too', async () => {
|
||||||
|
@ -25,8 +25,7 @@ import { AppInfo } from 'wasm-lib/kcl/bindings/AppInfo'
|
|||||||
import { CoreDumpManager } from 'lib/coredump'
|
import { CoreDumpManager } from 'lib/coredump'
|
||||||
import openWindow from 'lib/openWindow'
|
import openWindow from 'lib/openWindow'
|
||||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||||
import { rangeTypeFix } from './workers/types'
|
import { TEST } from 'env'
|
||||||
import { parser } from 'lib/singletons'
|
|
||||||
|
|
||||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||||
export type { Value } from '../wasm-lib/kcl/bindings/Value'
|
export type { Value } from '../wasm-lib/kcl/bindings/Value'
|
||||||
@ -97,26 +96,23 @@ export const wasmUrl = () => {
|
|||||||
|
|
||||||
// Initialise the wasm module.
|
// Initialise the wasm module.
|
||||||
const initialise = async () => {
|
const initialise = async () => {
|
||||||
|
try {
|
||||||
const fullUrl = wasmUrl()
|
const fullUrl = wasmUrl()
|
||||||
const input = await fetch(fullUrl)
|
const input = await fetch(fullUrl)
|
||||||
const buffer = await input.arrayBuffer()
|
const buffer = await input.arrayBuffer()
|
||||||
return init(buffer)
|
return await init(buffer)
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error initialising WASM', e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initPromise = initialise()
|
export const initPromise = initialise()
|
||||||
|
|
||||||
export type PathToNode = [string | number, string][]
|
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
||||||
|
ranges.map(([start, end]) => [start, end])
|
||||||
|
|
||||||
interface Memory {
|
export const parse = (code: string): Program => {
|
||||||
[key: string]: MemoryItem
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProgramMemory {
|
|
||||||
root: Memory
|
|
||||||
return: ProgramReturn | null
|
|
||||||
}
|
|
||||||
|
|
||||||
/*export const parse = (code: string): Program => {
|
|
||||||
try {
|
try {
|
||||||
const program: Program = parse_wasm(code)
|
const program: Program = parse_wasm(code)
|
||||||
return program
|
return program
|
||||||
@ -131,10 +127,17 @@ export interface ProgramMemory {
|
|||||||
console.log(kclError)
|
console.log(kclError)
|
||||||
throw kclError
|
throw kclError
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
export const parse = async (code: string): Promise<Program> => {
|
export type PathToNode = [string | number, string][]
|
||||||
return parser.parse(code)
|
|
||||||
|
interface Memory {
|
||||||
|
[key: string]: MemoryItem
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProgramMemory {
|
||||||
|
root: Memory
|
||||||
|
return: ProgramReturn | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const executor = async (
|
export const executor = async (
|
||||||
@ -156,10 +159,6 @@ export const executor = async (
|
|||||||
return _programMemory
|
return _programMemory
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSettingsState = import('components/SettingsAuthProvider').then(
|
|
||||||
(module) => module.getSettingsState
|
|
||||||
)
|
|
||||||
|
|
||||||
export const _executor = async (
|
export const _executor = async (
|
||||||
node: Program,
|
node: Program,
|
||||||
programMemory: ProgramMemory = { root: {}, return: null },
|
programMemory: ProgramMemory = { root: {}, return: null },
|
||||||
@ -167,8 +166,14 @@ export const _executor = async (
|
|||||||
isMock: boolean
|
isMock: boolean
|
||||||
): Promise<ProgramMemory> => {
|
): Promise<ProgramMemory> => {
|
||||||
try {
|
try {
|
||||||
const baseUnit =
|
let baseUnit = 'mm'
|
||||||
|
if (!TEST) {
|
||||||
|
const getSettingsState = import('components/SettingsAuthProvider').then(
|
||||||
|
(module) => module.getSettingsState
|
||||||
|
)
|
||||||
|
baseUnit =
|
||||||
(await getSettingsState)()?.modeling.defaultUnit.current || 'mm'
|
(await getSettingsState)()?.modeling.defaultUnit.current || 'mm'
|
||||||
|
}
|
||||||
const memory: ProgramMemory = await execute_wasm(
|
const memory: ProgramMemory = await execute_wasm(
|
||||||
JSON.stringify(node),
|
JSON.stringify(node),
|
||||||
JSON.stringify(programMemory),
|
JSON.stringify(programMemory),
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
import init, { parse_wasm } from 'wasm-lib/pkg/wasm_lib'
|
|
||||||
import { Program } from 'lang/wasm'
|
|
||||||
import { KclError as RustKclError } from 'wasm-lib/kcl/bindings/KclError'
|
|
||||||
import { KCLError } from 'lang/errors'
|
|
||||||
import {
|
|
||||||
WasmWorkerEventType,
|
|
||||||
WasmWorkerEvent,
|
|
||||||
WasmWorkerOptions,
|
|
||||||
ParserWorkerCall,
|
|
||||||
rangeTypeFix,
|
|
||||||
} from 'lang/workers/types'
|
|
||||||
|
|
||||||
// Initialise the wasm module.
|
|
||||||
const initialise = async (wasmUrl: string) => {
|
|
||||||
const input = await fetch(wasmUrl)
|
|
||||||
const buffer = await input.arrayBuffer()
|
|
||||||
return init(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
onmessage = function (event) {
|
|
||||||
const { worker, eventType, eventData }: WasmWorkerEvent = event.data
|
|
||||||
|
|
||||||
switch (eventType) {
|
|
||||||
case WasmWorkerEventType.Init:
|
|
||||||
let { wasmUrl }: WasmWorkerOptions = eventData as WasmWorkerOptions
|
|
||||||
initialise(wasmUrl)
|
|
||||||
.then((instantiatedModule) => {
|
|
||||||
console.log('Worker: WASM module loaded', worker, instantiatedModule)
|
|
||||||
postMessage({
|
|
||||||
eventType: WasmWorkerEventType.Init,
|
|
||||||
response: { worker: worker, initialized: true },
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('Worker: Error loading wasm module', worker, error)
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case WasmWorkerEventType.Call:
|
|
||||||
const data = eventData as ParserWorkerCall
|
|
||||||
try {
|
|
||||||
const program: Program = parse_wasm(data.code)
|
|
||||||
postMessage({ uuid: data.uuid, response: program })
|
|
||||||
} catch (e: any) {
|
|
||||||
const parsed: RustKclError = JSON.parse(e.toString())
|
|
||||||
const kclError = new KCLError(
|
|
||||||
parsed.kind,
|
|
||||||
parsed.msg,
|
|
||||||
rangeTypeFix(parsed.sourceRanges)
|
|
||||||
)
|
|
||||||
|
|
||||||
postMessage({
|
|
||||||
eventType: WasmWorkerEventType.Call,
|
|
||||||
response: { uuid: data.uuid, response: kclError },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
console.error('Worker: Unknown message type', worker, eventType)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
import { KCLError } from 'lang/errors'
|
|
||||||
import { Program } from 'lang/wasm'
|
|
||||||
|
|
||||||
export enum WasmWorker {
|
|
||||||
Parser = 'parser',
|
|
||||||
}
|
|
||||||
export enum WasmWorkerEventType {
|
|
||||||
Init = 'init',
|
|
||||||
Call = 'call',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WasmWorkerOptions {
|
|
||||||
wasmUrl: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ParserWorkerCall {
|
|
||||||
uuid: string
|
|
||||||
code: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ParserWorkerInitResponse {
|
|
||||||
worker: WasmWorker
|
|
||||||
initialized: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ParserWorkerCallResponse {
|
|
||||||
uuid: string
|
|
||||||
response: Program | KCLError
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ParserWorkerResponse {
|
|
||||||
eventType: WasmWorkerEventType
|
|
||||||
response: ParserWorkerInitResponse | ParserWorkerCallResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WasmWorkerEvent {
|
|
||||||
eventType: WasmWorkerEventType
|
|
||||||
eventData: Uint8Array | WasmWorkerOptions | ParserWorkerCall
|
|
||||||
worker: WasmWorker
|
|
||||||
}
|
|
||||||
|
|
||||||
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
|
||||||
ranges.map(([start, end]) => [start, end])
|
|
@ -103,6 +103,7 @@ export const fileLoader: LoaderFunction = async ({
|
|||||||
// Update both the state and the editor's code.
|
// Update both the state and the editor's code.
|
||||||
// We explicitly do not write to the file here since we are loading from
|
// We explicitly do not write to the file here since we are loading from
|
||||||
// the file system and not the editor.
|
// the file system and not the editor.
|
||||||
|
codeManager.updateCurrentFilePath(currentFilePath)
|
||||||
codeManager.updateCodeStateEditor(code)
|
codeManager.updateCodeStateEditor(code)
|
||||||
kclManager.executeCode(true)
|
kclManager.executeCode(true)
|
||||||
|
|
||||||
|
@ -119,9 +119,9 @@ export async function getEventForSelectWithPoint(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEventForSegmentSelection(
|
export function getEventForSegmentSelection(
|
||||||
obj: Object3D<Object3DEventMap>
|
obj: Object3D<Object3DEventMap>
|
||||||
): Promise<ModelingMachineEvent | null> {
|
): ModelingMachineEvent | null {
|
||||||
const group = getParentGroup(obj, [
|
const group = getParentGroup(obj, [
|
||||||
STRAIGHT_SEGMENT,
|
STRAIGHT_SEGMENT,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
@ -143,7 +143,7 @@ export async function getEventForSegmentSelection(
|
|||||||
// previous drags don't update ast for efficiency reasons
|
// previous drags don't update ast for efficiency reasons
|
||||||
// So we want to make sure we have and updated ast with
|
// So we want to make sure we have and updated ast with
|
||||||
// accurate source ranges
|
// accurate source ranges
|
||||||
const updatedAst = await parse(codeManager.code)
|
const updatedAst = parse(codeManager.code)
|
||||||
const node = getNodeFromPath<CallExpression>(
|
const node = getNodeFromPath<CallExpression>(
|
||||||
updatedAst,
|
updatedAst,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
@ -254,10 +254,10 @@ export function processCodeMirrorRanges({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
||||||
let updated: Program
|
let updated: Program
|
||||||
try {
|
try {
|
||||||
updated = await parse(recast(kclManager.ast))
|
updated = parse(recast(kclManager.ast))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('error parsing code in processCodeMirrorRanges', e)
|
console.error('error parsing code in processCodeMirrorRanges', e)
|
||||||
return
|
return
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { SceneEntities } from 'clientSideScene/sceneEntities'
|
import { SceneEntities } from 'clientSideScene/sceneEntities'
|
||||||
import { SceneInfra } from 'clientSideScene/sceneInfra'
|
import { SceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
|
import EditorManager from 'editor/manager'
|
||||||
import { KclManager } from 'lang/KclSingleton'
|
import { KclManager } from 'lang/KclSingleton'
|
||||||
import CodeManager from 'lang/codeManager'
|
import CodeManager from 'lang/codeManager'
|
||||||
import Parser from 'lang/parser'
|
|
||||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
|
|
||||||
export const parser = new Parser()
|
|
||||||
export const codeManager = new CodeManager()
|
export const codeManager = new CodeManager()
|
||||||
|
|
||||||
export const engineCommandManager = new EngineCommandManager()
|
export const engineCommandManager = new EngineCommandManager()
|
||||||
|
|
||||||
|
// This needs to be after codeManager is created.
|
||||||
export const kclManager = new KclManager(engineCommandManager)
|
export const kclManager = new KclManager(engineCommandManager)
|
||||||
engineCommandManager.getAstCb = () => kclManager.ast
|
engineCommandManager.getAstCb = () => kclManager.ast
|
||||||
|
|
||||||
@ -17,3 +17,6 @@ export const sceneInfra = new SceneInfra(engineCommandManager)
|
|||||||
engineCommandManager.camControlsCameraChange = sceneInfra.onCameraChange
|
engineCommandManager.camControlsCameraChange = sceneInfra.onCameraChange
|
||||||
|
|
||||||
export const sceneEntitiesManager = new SceneEntities(engineCommandManager)
|
export const sceneEntitiesManager = new SceneEntities(engineCommandManager)
|
||||||
|
|
||||||
|
// This needs to be after sceneInfra and engineCommandManager are is created.
|
||||||
|
export const editorManager = new EditorManager()
|
||||||
|
@ -82,7 +82,7 @@ export function useCalculateKclExpression({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const execAstAndSetResult = async () => {
|
const execAstAndSetResult = async () => {
|
||||||
const code = `const __result__ = ${value}`
|
const code = `const __result__ = ${value}`
|
||||||
const ast = await parse(code)
|
const ast = parse(code)
|
||||||
const _programMem: any = { root: {}, return: null }
|
const _programMem: any = { root: {}, return: null }
|
||||||
availableVarInfo.variables.forEach(({ key, value }) => {
|
availableVarInfo.variables.forEach(({ key, value }) => {
|
||||||
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { Coords2d } from 'lang/std/sketch'
|
import { Coords2d } from 'lang/std/sketch'
|
||||||
import { isPointsCCW, initPromise } from 'lang/wasm'
|
import { isPointsCCW, initPromise } from 'lang/wasm'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('test isPointsCW', () => {
|
describe('test isPointsCW', () => {
|
||||||
test('basic test', () => {
|
test('basic test', () => {
|
||||||
|
@ -16,6 +16,54 @@ export type CommandBarContext = {
|
|||||||
argumentsToSubmit: { [x: string]: unknown }
|
argumentsToSubmit: { [x: string]: unknown }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CommandBarMachineEvent =
|
||||||
|
| { type: 'Open' }
|
||||||
|
| { type: 'Close' }
|
||||||
|
| { type: 'Clear' }
|
||||||
|
| {
|
||||||
|
type: 'Select command'
|
||||||
|
data: { command: Command }
|
||||||
|
}
|
||||||
|
| { type: 'Deselect command' }
|
||||||
|
| { type: 'Submit command'; data: { [x: string]: unknown } }
|
||||||
|
| {
|
||||||
|
type: 'Add argument'
|
||||||
|
data: { argument: CommandArgumentWithName<unknown> }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'Remove argument'
|
||||||
|
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'Edit argument'
|
||||||
|
data: { arg: CommandArgumentWithName<unknown> }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'Add commands'
|
||||||
|
data: { commands: Command[] }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'Remove commands'
|
||||||
|
data: { commands: Command[] }
|
||||||
|
}
|
||||||
|
| { type: 'Submit argument'; data: { [x: string]: unknown } }
|
||||||
|
| {
|
||||||
|
type: 'done.invoke.validateArguments'
|
||||||
|
data: { [x: string]: unknown }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'error.platform.validateArguments'
|
||||||
|
data: { message: string; arg: CommandArgumentWithName<unknown> }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'Find and select command'
|
||||||
|
data: { name: string; ownerMachine: string }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'Change current argument'
|
||||||
|
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||||
|
}
|
||||||
|
|
||||||
export const commandBarMachine = createMachine(
|
export const commandBarMachine = createMachine(
|
||||||
{
|
{
|
||||||
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAJwA6AGwAmAKwBmBoukAWafIAcDcSoA0IAJ6JZDaZIDs8hgzV6AjA61a1DWQF8PhtJlwFiciowUkYWJBAOWB4+AQiRBFF5CwdpcVkHS1lpVyU5QxNEh1lFGTUsrUUtOQd5SwZLLx8MbDwiUkkqGkgyAHk2MBwwwSiY-kEEswZJbPltM0U3eXLZAsRFeQcrerUHRbTFvfkmkF9WgI6u2ggyADFONv98WkowAGNufDeW-2GI0d443iiHESkktUUilkskqdiUWjWCAcDC0kjUqnElkc6lkoK0JzOT0CnWo1zIAEEIARvn48LA-uwuIC4qAEqIHJjJPJcYtxFoYdJFFjVsZEG5pqCquJxJoGvJxOUCT82sSrj0AEpgdCoABuYC+yog9OYIyZsQmYmhWzMmTqLnU0kyikRGVRbj5lg2SmUam5StpFxIkgAymBXh8HlADQGyKHw58aecGZEzUDWYgchY7CisWoSlpQQ5EVDLJJxKkdIpyypUop-ed2kHCW0Xu9uD1kwDzcCEJCtlUNgWhZZ1OlnaKEBpZJItHVzDZ5xlpPWiZdDc8w22Ow5wozosyLUiVNMSg5ytVKmfrIipzO564z2otPVpI1vKd18SAOJYbgACzAEhI3wUgoAAV3QQZuFgCg-1wGAvjAkgSCgkCSHAyCcG4TtUxZYRED2TQZGSOw80qaQ7ARCc9mmZYSksextGkNJBRXFUOh-f9AOA0CIKgmCABE4E3D5oyTE1-lww8clKBjZF2Bh5SFZwXUUyRVDzJwNE2LQzzYwNJE4gCgJwKNeMw6DJHJAB3LAYlM-AHjYMDuFjMCACN0B4NCMKgnD927dMkWSUs8yY8RISfWQshvcsrDlapshULJPHfZsDKM7iHPM-jJAANSwShOAgX9IzICB+DASQHh1VAAGsqp1Qrit-MByXQvisP8sY8ISZwbCsDllBLSwz1qRFBQsRZ5WRIVkui-TG0M39jJ4jqLNgfLmpK3hTLIQCSFQIM2EoX8ADMjvQSQmqKna2vWvyJL3HrD1SEoyzSdJUkUm9dnUtQn10aK6hkxbiU1HVODAay3M87zE1+J6UwCtN8IQUauR0OZ0ksFwUUqRFPWmUdouPXR3TBjoIahmHKQIHKuqRrtUYSaVShhLF+TkeTzBFQosVKDRM2RVInEdSmg2p6GyE1bU9R8zrsKZqSexFqQHWlHNXHzCbFhnX1+UydFkVxiXJClmGAFEIG8hmld3ZGXp7TR5C5RSCxdjlQV1tR9bqaVdhsSFxDN5AALeOrgPa3ysJgiqcCqmr6sa7bWujxXjQd5nesQZYthsblIQYJx6hUic9AsdEFT2HQzByVLmgDJaw-eSOHPTjbysq6qcFqhrrtT9sO-4ugd1NFGc4QXEPo5eVLGsFxlnKREXGnZI9HcT1mOikO0s-S5w7bqNh9j-bkKOyQTvOy6B9utOHtj7qD1V8pSyrHJZ3STY4QmjQ0R2Ww0oSjuF3u+HAqAIBwEEOlRs48nZBVENkKQNprC42qBoJ0LothaXMJ9aElRXDLj3k3VUpJIBwOfgg4oMx8y41SPUOY8p5DFk2GiKoxsoRVmhGoM2cY2zAQRngChgU0a7HZtFH0ilRp1D5usVh6JXCKD2CUdI8lQ7rlbB8chkkJ6HkxFsBeulbQZAUCwrYCiqzIhyE+fqZtMomTMg-aCwiWYEV2NOBUCghQ6EFGzYsvpwQUT5MxawFECx2JWllRxMdLI2TsrtKMTkXIuMnmeCiZYwrePMFWCKv1XZKVGroWwmxNARK4g4hWG0tp3wSSk16lQpC41ULjV8uxUjSBvFCNEDTdCOkWCY44xCGzgzAJDaGdTnaZHBADYcqg5DpCovzeRno5izA0BeUOh8o5OPgDo+BaNvqV0xNFaE7gdYTnnvkmUChdKEMyF4LwQA */
|
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAJwA6AGwAmAKwBmBoukAWafIAcDcSoA0IAJ6JZDaZIDs8hgzV6AjA61a1DWQF8PhtJlwFiciowUkYWJBAOWB4+AQiRBFF5CwdpcVkHS1lpVyU5QxNEh1lFGTUsrUUtOQd5SwZLLx8MbDwiUkkqGkgyAHk2MBwwwSiY-kEEswZJbPltM0U3eXLZAsRFeQcrerUHRbTFvfkmkF9WgI6u2ggyADFONv98WkowAGNufDeW-2GI0d443iiHESkktUUilkskqdiUWjWCAcDC0kjUqnElkc6lkoK0JzOT0CnWo1zIAEEIARvn48LA-uwuIC4qAEqIHJjJPJcYtxFoYdJFFjVsZEG5pqCquJxJoGvJxOUCT82sSrj0AEpgdCoABuYC+yog9OYIyZsQmYmhWzMmTqLnU0kyikRGVRbj5lg2SmUam5StpFxIkgAymBXh8HlADQGyKHw58aecGZEzUDWYgchY7CisWoSlpQQ5EVDLJJxKkdIpyypUop-ed2kHCW0Xu9uD1kwDzcCEJCtlUNgWhZZ1OlnaKEBpZJItHVzDZ5xlpPWiZdDc8w22Ow5wozosyLUiVNMSg5ytVKmfrIipzO564z2otPVpI1vKd18SAOJYbgACzAEhI3wUgoAAV3QQZuFgCg-1wGAvjAkgSCgkCSHAyCcG4TtUxZYRED2TQZGSOw80qaQ7ARCc9mmZYSksextGkNJBRXFUOh-f9AOA0CIKgmCABE4E3D5oyTE1-lww8clKBjZF2Bh5SFZwXUUyRVDzJwNE2LQzzYwNJE4gCgJwKNeMw6DJHJAB3LAYlM-AHjYMDuFjMCACN0B4NCMKgnD927dMkWSUs8yY8RISfWQshvcsrDlapshULJPHfZsDKM7iHPM-jJAANSwShOAgX9IzICB+DASQHh1VAAGsqp1Qrit-MByXQvisP8sY8ISZwbCsDllBLSwz1qRFBQsRZ5WRIVkui-TG0M39jJ4jqLNgfLmpK3hTLIQCSFQIM2EoX8ADMjvQSQmqKna2vWvyJL3HrD1SEoyzSdJUkUm9dnUtQn10aK6hkxbiU1HVODAay3M87zE1+J6UwCtN8IQUauR0OZ0ksFwUUqRFPWmUdouPXR3TBjoIahmHKQIHKuqRrtUYSaVShhLF+TkeTzBFQosVKDRM2RVInEdSmg2p6GyE1bU9R8zrsKZqSexFqQHWlHNXHzCbFhnX1+UydFkVxiXJClmGAFEIG8hmld3ZGXp7TR5C5RSCxdjlQV1tR9bqaVdhsSFxDN5AALeOrgPa3ysJgiqcCqmr6sa7bWujxXjQd5nesQZYthsblIQYJx6hUic9AsdEFT2HQzByVLmgDJaw-eSOHPTjbysq6qcFqhrrtT9sO-4ugd1NFGc4QXEPo5eVLGsFxlnKREXGnZI9HcT1mOikO0s-S5w7bqNh9j-bkKOyQTvOy6B9utOHtj7qD1V8pSyrHJZ3STY4QmjQ0R2Ww0oSjuF3u+HAqAIBwEEOlRs48nZBVENkKQNprC42qBoJ0LothaXMJ9aElRXDLj3k3VUpJIBwOfgg4oMx8y41SPUOY8p5DFk2GiKoxsoRVmhGoM2cY2zAQRngChgU0a7HZtFH0ilRp1D5usVh6JXCKD2CUdI8lQ7rlbB8chkkJ6HkxFsBeulbQZAUCwrYCiqzIhyE+fqZtMomTMg-aCwiWYEV2NOBUCghQ6EFGzYsvpwQUT5MxawFECx2JWllRxMdLI2TsrtKMTkXIuMnmeCiZYwrePMFWCKv1XZKVGroWwmxNARK4g4hWG0tp3wSSk16lQpC41ULjV8uxUjSBvFCNEDTdCOkWCY44xCGzgzAJDaGdTnaZHBADYcqg5DpCovzeRno5izA0BeUOh8o5OPgDo+BaNvqV0xNFaE7gdYTnnvkmUChdKEMyF4LwQA */
|
||||||
@ -227,53 +275,7 @@ export const commandBarMachine = createMachine(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
events: {} as
|
events: {} as CommandBarMachineEvent,
|
||||||
| { type: 'Open' }
|
|
||||||
| { type: 'Close' }
|
|
||||||
| { type: 'Clear' }
|
|
||||||
| {
|
|
||||||
type: 'Select command'
|
|
||||||
data: { command: Command }
|
|
||||||
}
|
|
||||||
| { type: 'Deselect command' }
|
|
||||||
| { type: 'Submit command'; data: { [x: string]: unknown } }
|
|
||||||
| {
|
|
||||||
type: 'Add argument'
|
|
||||||
data: { argument: CommandArgumentWithName<unknown> }
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'Remove argument'
|
|
||||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'Edit argument'
|
|
||||||
data: { arg: CommandArgumentWithName<unknown> }
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'Add commands'
|
|
||||||
data: { commands: Command[] }
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'Remove commands'
|
|
||||||
data: { commands: Command[] }
|
|
||||||
}
|
|
||||||
| { type: 'Submit argument'; data: { [x: string]: unknown } }
|
|
||||||
| {
|
|
||||||
type: 'done.invoke.validateArguments'
|
|
||||||
data: { [x: string]: unknown }
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'error.platform.validateArguments'
|
|
||||||
data: { message: string; arg: CommandArgumentWithName<unknown> }
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'Find and select command'
|
|
||||||
data: { name: string; ownerMachine: string }
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'Change current argument'
|
|
||||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
preserveActionOrder: true,
|
preserveActionOrder: true,
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
sceneInfra,
|
sceneInfra,
|
||||||
sceneEntitiesManager,
|
sceneEntitiesManager,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
|
editorManager,
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import {
|
import {
|
||||||
horzVertInfo,
|
horzVertInfo,
|
||||||
@ -800,7 +801,7 @@ export const modelingMachine = createMachine(
|
|||||||
sketchDetails.origin
|
sketchDetails.origin
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
'AST extrude': (_, event) => {
|
'AST extrude': async (_, event) => {
|
||||||
if (!event.data) return
|
if (!event.data) return
|
||||||
const { selection, distance } = event.data
|
const { selection, distance } = event.data
|
||||||
let ast = kclManager.ast
|
let ast = kclManager.ast
|
||||||
@ -829,10 +830,12 @@ export const modelingMachine = createMachine(
|
|||||||
? distance.variableIdentifierAst
|
? distance.variableIdentifierAst
|
||||||
: distance.valueAst
|
: distance.valueAst
|
||||||
)
|
)
|
||||||
// TODO not handling focusPath correctly I think
|
const selections = await kclManager.updateAst(modifiedAst, true, {
|
||||||
kclManager.updateAst(modifiedAst, true, {
|
|
||||||
focusPath: pathToExtrudeArg,
|
focusPath: pathToExtrudeArg,
|
||||||
})
|
})
|
||||||
|
if (selections) {
|
||||||
|
editorManager.selectRange(selections)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'conditionally equip line tool': (_, { type }) => {
|
'conditionally equip line tool': (_, { type }) => {
|
||||||
if (type === 'done.invoke.animate-to-face') {
|
if (type === 'done.invoke.animate-to-face') {
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
import { persist } from 'zustand/middleware'
|
import { persist } from 'zustand/middleware'
|
||||||
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
|
||||||
import {
|
import {
|
||||||
Program,
|
Program,
|
||||||
_executor,
|
_executor,
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
programMemoryInit,
|
programMemoryInit,
|
||||||
} from './lang/wasm'
|
} from './lang/wasm'
|
||||||
import { Selection } from 'lib/selections'
|
|
||||||
import { enginelessExecutor } from './lib/testHelpers'
|
import { enginelessExecutor } from './lib/testHelpers'
|
||||||
import { EngineCommandManager } from './lang/std/engineConnection'
|
import { EngineCommandManager } from './lang/std/engineConnection'
|
||||||
import { KCLError } from './lang/errors'
|
import { KCLError } from './lang/errors'
|
||||||
@ -55,12 +53,6 @@ export type PaneType =
|
|||||||
| 'lspMessages'
|
| 'lspMessages'
|
||||||
|
|
||||||
export interface StoreState {
|
export interface StoreState {
|
||||||
editorView: EditorView | null
|
|
||||||
setEditorView: (editorView: EditorView) => void
|
|
||||||
highlightRange: [number, number]
|
|
||||||
setHighlightRange: (range: Selection['range']) => void
|
|
||||||
isShiftDown: boolean
|
|
||||||
setIsShiftDown: (isShiftDown: boolean) => void
|
|
||||||
mediaStream?: MediaStream
|
mediaStream?: MediaStream
|
||||||
setMediaStream: (mediaStream: MediaStream) => void
|
setMediaStream: (mediaStream: MediaStream) => void
|
||||||
isStreamReady: boolean
|
isStreamReady: boolean
|
||||||
@ -92,34 +84,12 @@ export interface StoreState {
|
|||||||
path: string
|
path: string
|
||||||
}[]
|
}[]
|
||||||
setHomeMenuItems: (items: { name: string; path: string }[]) => void
|
setHomeMenuItems: (items: { name: string; path: string }[]) => void
|
||||||
lastCodeMirrorSelectionUpdatedFromScene: number
|
|
||||||
setLastCodeMirrorSelectionUpdatedFromScene: (time: number) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStore = create<StoreState>()(
|
export const useStore = create<StoreState>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => {
|
(set, get) => {
|
||||||
return {
|
return {
|
||||||
editorView: null,
|
|
||||||
setEditorView: (editorView) => {
|
|
||||||
set({ editorView })
|
|
||||||
},
|
|
||||||
highlightRange: [0, 0],
|
|
||||||
setHighlightRange: (selection) => {
|
|
||||||
set({ highlightRange: selection })
|
|
||||||
const editorView = get().editorView
|
|
||||||
const safeEnd = Math.min(
|
|
||||||
selection[1],
|
|
||||||
editorView?.state.doc.length || selection[1]
|
|
||||||
)
|
|
||||||
if (editorView) {
|
|
||||||
editorView.dispatch({
|
|
||||||
effects: addLineHighlight.of([selection[0], safeEnd]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isShiftDown: false,
|
|
||||||
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
|
|
||||||
setMediaStream: (mediaStream) => set({ mediaStream }),
|
setMediaStream: (mediaStream) => set({ mediaStream }),
|
||||||
isStreamReady: false,
|
isStreamReady: false,
|
||||||
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
|
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
|
||||||
@ -159,9 +129,6 @@ export const useStore = create<StoreState>()(
|
|||||||
setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
|
setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
|
||||||
homeMenuItems: [],
|
homeMenuItems: [],
|
||||||
setHomeMenuItems: (homeMenuItems) => set({ homeMenuItems }),
|
setHomeMenuItems: (homeMenuItems) => set({ homeMenuItems }),
|
||||||
lastCodeMirrorSelectionUpdatedFromScene: Date.now(),
|
|
||||||
setLastCodeMirrorSelectionUpdatedFromScene: (time) =>
|
|
||||||
set({ lastCodeMirrorSelectionUpdatedFromScene: time }),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
@ -4,13 +4,14 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"/*": ["src/*"]
|
"/*": ["src/*"]
|
||||||
},
|
},
|
||||||
"types": ["vite/client", "@types/wicg-file-system-access", "node", "@wdio/globals/types"],
|
"types": [
|
||||||
"target": "esnext",
|
"vite/client",
|
||||||
"lib": [
|
"@types/wicg-file-system-access",
|
||||||
"dom",
|
"node",
|
||||||
"dom.iterable",
|
"@wdio/globals/types"
|
||||||
"esnext"
|
|
||||||
],
|
],
|
||||||
|
"target": "esnext",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@ -25,10 +26,6 @@
|
|||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src", "e2e", "./*.ts"],
|
||||||
"src",
|
|
||||||
"e2e",
|
|
||||||
"./*.ts"
|
|
||||||
],
|
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,5 @@
|
|||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["vite.config.ts"]
|
||||||
"vite.config.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
@ -1,15 +1,9 @@
|
|||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
import viteTsconfigPaths from 'vite-tsconfig-paths'
|
import viteTsconfigPaths from 'vite-tsconfig-paths'
|
||||||
import eslint from 'vite-plugin-eslint'
|
import eslint from 'vite-plugin-eslint'
|
||||||
import dns from 'dns'
|
|
||||||
import { defineConfig, configDefaults } from 'vitest/config'
|
import { defineConfig, configDefaults } from 'vitest/config'
|
||||||
import version from 'vite-plugin-package-version'
|
import version from 'vite-plugin-package-version'
|
||||||
|
|
||||||
// Only needed because we run Node < 17
|
|
||||||
// and we want to open `localhost` not `127.0.0.1` on server start
|
|
||||||
// reference: https://vitejs.dev/config/server-options.html#server-host
|
|
||||||
dns.setDefaultResultOrder('verbatim')
|
|
||||||
|
|
||||||
const config = defineConfig({
|
const config = defineConfig({
|
||||||
server: {
|
server: {
|
||||||
open: true,
|
open: true,
|
||||||
@ -25,32 +19,38 @@ const config = defineConfig({
|
|||||||
forks: {
|
forks: {
|
||||||
maxForks: 2,
|
maxForks: 2,
|
||||||
minForks: 1,
|
minForks: 1,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setupFiles: 'src/setupTests.ts',
|
},
|
||||||
|
setupFiles: ['src/setupTests.ts', '@vitest/web-worker'],
|
||||||
environment: 'happy-dom',
|
environment: 'happy-dom',
|
||||||
coverage: {
|
coverage: {
|
||||||
provider: 'istanbul' // or 'v8'
|
provider: 'istanbul', // or 'v8'
|
||||||
},
|
},
|
||||||
exclude: [...configDefaults.exclude, '**/e2e/playwright/**/*'],
|
exclude: [...configDefaults.exclude, '**/e2e/playwright/**/*'],
|
||||||
deps: {
|
deps: {
|
||||||
inline: ['vitest-canvas-mock']
|
optimizer: {
|
||||||
}
|
web: {
|
||||||
|
include: ['vitest-canvas-mock'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
clearMocks: true,
|
||||||
|
restoreMocks: true,
|
||||||
|
mockReset: true,
|
||||||
|
reporters: process.env.GITHUB_ACTIONS
|
||||||
|
? ['dot', 'github-actions']
|
||||||
|
: ['verbose', 'hanging-process'],
|
||||||
|
testTimeout: 1000,
|
||||||
|
hookTimeout: 1000,
|
||||||
|
teardownTimeout: 1000,
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: 'build',
|
outDir: 'build',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [react(), viteTsconfigPaths(), eslint(), version()],
|
||||||
react(),
|
|
||||||
viteTsconfigPaths(),
|
|
||||||
eslint(),
|
|
||||||
version(),
|
|
||||||
],
|
|
||||||
worker: {
|
worker: {
|
||||||
plugins: () => [
|
plugins: () => [viteTsconfigPaths()],
|
||||||
viteTsconfigPaths(),
|
},
|
||||||
],
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
|
70
yarn.lock
@ -1675,10 +1675,10 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
|
|
||||||
"@headlessui/react@^1.7.18":
|
"@headlessui/react@^1.7.19":
|
||||||
version "1.7.18"
|
version "1.7.19"
|
||||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.18.tgz#30af4634d2215b2ca1aa29d07f33d02bea82d9d7"
|
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.19.tgz#91c78cf5fcb254f4a0ebe96936d48421caf75f40"
|
||||||
integrity sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ==
|
integrity sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/react-virtual" "^3.0.0-beta.60"
|
"@tanstack/react-virtual" "^3.0.0-beta.60"
|
||||||
client-only "^0.0.1"
|
client-only "^0.0.1"
|
||||||
@ -1703,9 +1703,9 @@
|
|||||||
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
|
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
|
||||||
|
|
||||||
"@humanwhocodes/object-schema@^2.0.2":
|
"@humanwhocodes/object-schema@^2.0.2":
|
||||||
version "2.0.2"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
||||||
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
|
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
||||||
|
|
||||||
"@iarna/toml@^2.2.5":
|
"@iarna/toml@^2.2.5":
|
||||||
version "2.2.5"
|
version "2.2.5"
|
||||||
@ -2360,19 +2360,14 @@
|
|||||||
integrity sha512-awNxydYSU+E2vL7EiOAMtiSOfL5gUM5X4YSE2A92qpxDJQ/rXz6oMPYBFDcDywlUmvIDI6zsqgq17cGm5CITQw==
|
integrity sha512-awNxydYSU+E2vL7EiOAMtiSOfL5gUM5X4YSE2A92qpxDJQ/rXz6oMPYBFDcDywlUmvIDI6zsqgq17cGm5CITQw==
|
||||||
|
|
||||||
"@types/eslint@^8.4.5":
|
"@types/eslint@^8.4.5":
|
||||||
version "8.44.1"
|
version "8.56.10"
|
||||||
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.1.tgz#d1811559bb6bcd1a76009e3f7883034b78a0415e"
|
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d"
|
||||||
integrity sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==
|
integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/estree" "*"
|
"@types/estree" "*"
|
||||||
"@types/json-schema" "*"
|
"@types/json-schema" "*"
|
||||||
|
|
||||||
"@types/estree@*":
|
"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0":
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
|
|
||||||
integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
|
|
||||||
|
|
||||||
"@types/estree@1.0.5", "@types/estree@^1.0.0":
|
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
|
||||||
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
||||||
@ -2417,7 +2412,12 @@
|
|||||||
expect "^29.0.0"
|
expect "^29.0.0"
|
||||||
pretty-format "^29.0.0"
|
pretty-format "^29.0.0"
|
||||||
|
|
||||||
"@types/json-schema@*", "@types/json-schema@^7.0.9":
|
"@types/json-schema@*":
|
||||||
|
version "7.0.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||||
|
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||||
|
|
||||||
|
"@types/json-schema@^7.0.9":
|
||||||
version "7.0.12"
|
version "7.0.12"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
||||||
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
||||||
@ -2764,6 +2764,13 @@
|
|||||||
loupe "^2.3.7"
|
loupe "^2.3.7"
|
||||||
pretty-format "^29.7.0"
|
pretty-format "^29.7.0"
|
||||||
|
|
||||||
|
"@vitest/web-worker@^1.5.0":
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/web-worker/-/web-worker-1.5.0.tgz#9de0e20b4a9cfed2a4958d757bd0594a76a2d4e9"
|
||||||
|
integrity sha512-WxX5VAgp8knJGTZknTDUICAVtNiDsBDQN1z/ldsDFqhRUFp09WLhRxWnKqCQc+CXk1W+kpUZmWDjxaD3i8QBmA==
|
||||||
|
dependencies:
|
||||||
|
debug "^4.3.4"
|
||||||
|
|
||||||
"@wdio/cli@^8.24.3":
|
"@wdio/cli@^8.24.3":
|
||||||
version "8.24.3"
|
version "8.24.3"
|
||||||
resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-8.24.3.tgz#305adc66cd2264ed4ef4549266bbb327826e10ef"
|
resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-8.24.3.tgz#305adc66cd2264ed4ef4549266bbb327826e10ef"
|
||||||
@ -5080,11 +5087,12 @@ find-up@^6.3.0:
|
|||||||
path-exists "^5.0.0"
|
path-exists "^5.0.0"
|
||||||
|
|
||||||
flat-cache@^3.0.4:
|
flat-cache@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
|
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
|
||||||
integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
|
integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
|
||||||
dependencies:
|
dependencies:
|
||||||
flatted "^3.1.0"
|
flatted "^3.2.9"
|
||||||
|
keyv "^4.5.3"
|
||||||
rimraf "^3.0.2"
|
rimraf "^3.0.2"
|
||||||
|
|
||||||
flat@^5.0.2:
|
flat@^5.0.2:
|
||||||
@ -5092,10 +5100,10 @@ flat@^5.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
||||||
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
||||||
|
|
||||||
flatted@^3.1.0:
|
flatted@^3.2.9:
|
||||||
version "3.2.7"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
||||||
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
|
||||||
|
|
||||||
flux@^4.0.1:
|
flux@^4.0.1:
|
||||||
version "4.0.4"
|
version "4.0.4"
|
||||||
@ -5436,9 +5444,9 @@ globals@^11.1.0:
|
|||||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||||
|
|
||||||
globals@^13.19.0:
|
globals@^13.19.0:
|
||||||
version "13.20.0"
|
version "13.24.0"
|
||||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
|
resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
|
||||||
integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
|
integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
type-fest "^0.20.2"
|
type-fest "^0.20.2"
|
||||||
|
|
||||||
@ -8482,9 +8490,9 @@ tinybench@^2.5.1:
|
|||||||
integrity sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==
|
integrity sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==
|
||||||
|
|
||||||
tinypool@^0.8.3:
|
tinypool@^0.8.3:
|
||||||
version "0.8.3"
|
version "0.8.4"
|
||||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.3.tgz#e17d0a5315a7d425f875b05f7af653c225492d39"
|
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8"
|
||||||
integrity sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==
|
integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==
|
||||||
|
|
||||||
tinyspy@^2.2.0:
|
tinyspy@^2.2.0:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
|