This reverts commit 943cf21d34
.
@ -950,75 +950,7 @@ test(
|
|||||||
|
|
||||||
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
||||||
// FIXME: Skip on macos its being weird.
|
// FIXME: Skip on macos its being weird.
|
||||||
// test.skip(process.platform === 'darwin', 'Skip on macos')
|
test.skip(process.platform === 'darwin', 'Skip on macos')
|
||||||
|
|
||||||
test('Grid turned off to on via command bar', async ({ page }) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
const stream = page.getByTestId('stream')
|
|
||||||
const mask = [
|
|
||||||
page.locator('#app-header'),
|
|
||||||
page.locator('#sidebar-top-ribbon'),
|
|
||||||
page.locator('#sidebar-bottom-ribbon'),
|
|
||||||
]
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
await page.goto('/')
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
// wait for execution done
|
|
||||||
await expect(
|
|
||||||
page.locator('[data-message-type="execution-done"]')
|
|
||||||
).toHaveCount(1)
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
await u.closeKclCodePanel()
|
|
||||||
// TODO: Find a way to truly know that the objects have finished
|
|
||||||
// rendering, because an execution-done message is not sufficient.
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
// Open the command bar.
|
|
||||||
await page
|
|
||||||
.getByRole('button', { name: 'Commands', exact: false })
|
|
||||||
.or(page.getByRole('button', { name: '⌘K' }))
|
|
||||||
.click()
|
|
||||||
const commandName = 'show scale grid'
|
|
||||||
const commandOption = page.getByRole('option', {
|
|
||||||
name: commandName,
|
|
||||||
exact: false,
|
|
||||||
})
|
|
||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
|
||||||
// This selector changes after we set the setting
|
|
||||||
await cmdSearchBar.fill(commandName)
|
|
||||||
await expect(commandOption).toBeVisible()
|
|
||||||
await commandOption.click()
|
|
||||||
|
|
||||||
const toggleInput = page.getByPlaceholder('Off')
|
|
||||||
await expect(toggleInput).toBeVisible()
|
|
||||||
await expect(toggleInput).toBeFocused()
|
|
||||||
|
|
||||||
// Select On
|
|
||||||
await page.keyboard.press('ArrowDown')
|
|
||||||
await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute(
|
|
||||||
'data-headlessui-state',
|
|
||||||
'active selected'
|
|
||||||
)
|
|
||||||
await page.keyboard.press('ArrowUp')
|
|
||||||
await expect(page.getByRole('option', { name: 'On' })).toHaveAttribute(
|
|
||||||
'data-headlessui-state',
|
|
||||||
'active'
|
|
||||||
)
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Check the toast appeared
|
|
||||||
await expect(
|
|
||||||
page.getByText(`Set show scale grid to "true" as a user default`)
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
await expect(stream).toHaveScreenshot({
|
|
||||||
maxDiffPixels: 100,
|
|
||||||
mask,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Grid turned off', async ({ page }) => {
|
test('Grid turned off', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 54 KiB |
@ -69,7 +69,14 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const [isKclLspReady, setIsKclLspReady] = useState(false)
|
const [isKclLspReady, setIsKclLspReady] = useState(false)
|
||||||
const [isCopilotLspReady, setIsCopilotLspReady] = useState(false)
|
const [isCopilotLspReady, setIsCopilotLspReady] = useState(false)
|
||||||
|
|
||||||
const { auth } = useSettingsAuthContext()
|
const {
|
||||||
|
auth,
|
||||||
|
settings: {
|
||||||
|
context: {
|
||||||
|
modeling: { defaultUnit },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} = useSettingsAuthContext()
|
||||||
const token = auth?.context.token
|
const token = auth?.context.token
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
@ -85,6 +92,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const initEvent: KclWorkerOptions = {
|
const initEvent: KclWorkerOptions = {
|
||||||
wasmUrl: wasmUrl(),
|
wasmUrl: wasmUrl(),
|
||||||
token: token,
|
token: token,
|
||||||
|
baseUnit: defaultUnit.current,
|
||||||
apiBaseUrl: VITE_KC_API_BASE_URL,
|
apiBaseUrl: VITE_KC_API_BASE_URL,
|
||||||
}
|
}
|
||||||
lspWorker.postMessage({
|
lspWorker.postMessage({
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
sceneEntitiesManager,
|
sceneEntitiesManager,
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { IndexLoaderData } from 'lib/types'
|
import { IndexLoaderData } from 'lib/types'
|
||||||
import { settings } from 'lib/settings/initialSettings'
|
import { settings } from 'lib/settings/initialSettings'
|
||||||
import {
|
import {
|
||||||
@ -128,11 +129,27 @@ export const SettingsAuthProviderBase = ({
|
|||||||
.setTheme(context.app.theme.current)
|
.setTheme(context.app.theme.current)
|
||||||
.catch(reportRejection)
|
.catch(reportRejection)
|
||||||
},
|
},
|
||||||
|
setEngineScaleGridVisibility: ({ context }) => {
|
||||||
|
engineCommandManager.setScaleGridVisibility(
|
||||||
|
context.modeling.showScaleGrid.current
|
||||||
|
)
|
||||||
|
},
|
||||||
setClientTheme: ({ context }) => {
|
setClientTheme: ({ context }) => {
|
||||||
const opposingTheme = getOppositeTheme(context.app.theme.current)
|
const opposingTheme = getOppositeTheme(context.app.theme.current)
|
||||||
sceneInfra.theme = opposingTheme
|
sceneInfra.theme = opposingTheme
|
||||||
sceneEntitiesManager.updateSegmentBaseColor(opposingTheme)
|
sceneEntitiesManager.updateSegmentBaseColor(opposingTheme)
|
||||||
},
|
},
|
||||||
|
setEngineEdges: ({ context }) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
engineCommandManager.sendSceneCommand({
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'edge_lines_visible' as any, // TODO update kittycad.ts to get this new command type
|
||||||
|
hidden: !context.modeling.highlightEdges.current,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
toastSuccess: ({ event }) => {
|
toastSuccess: ({ event }) => {
|
||||||
if (!('data' in event)) return
|
if (!('data' in event)) return
|
||||||
const eventParts = event.type.replace(/^set./, '').split('.') as [
|
const eventParts = event.type.replace(/^set./, '').split('.') as [
|
||||||
@ -158,27 +175,17 @@ export const SettingsAuthProviderBase = ({
|
|||||||
},
|
},
|
||||||
'Execute AST': ({ context, event }) => {
|
'Execute AST': ({ context, event }) => {
|
||||||
try {
|
try {
|
||||||
const relevantSetting = (s: typeof settings) => {
|
|
||||||
return (
|
|
||||||
s.modeling?.defaultUnit?.current !==
|
|
||||||
context.modeling.defaultUnit.current ||
|
|
||||||
s.modeling.showScaleGrid.current !==
|
|
||||||
context.modeling.showScaleGrid.current ||
|
|
||||||
s.modeling?.highlightEdges.current !==
|
|
||||||
context.modeling.highlightEdges.current
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const allSettingsIncludesUnitChange =
|
const allSettingsIncludesUnitChange =
|
||||||
event.type === 'Set all settings' &&
|
event.type === 'Set all settings' &&
|
||||||
relevantSetting(event.settings)
|
event.settings?.modeling?.defaultUnit?.current !==
|
||||||
|
context.modeling.defaultUnit.current
|
||||||
const resetSettingsIncludesUnitChange =
|
const resetSettingsIncludesUnitChange =
|
||||||
event.type === 'Reset settings' && relevantSetting(settings)
|
event.type === 'Reset settings' &&
|
||||||
|
context.modeling.defaultUnit.current !==
|
||||||
|
settings?.modeling?.defaultUnit?.default
|
||||||
|
|
||||||
if (
|
if (
|
||||||
event.type === 'set.modeling.defaultUnit' ||
|
event.type === 'set.modeling.defaultUnit' ||
|
||||||
event.type === 'set.modeling.showScaleGrid' ||
|
|
||||||
event.type === 'set.modeling.highlightEdges' ||
|
|
||||||
allSettingsIncludesUnitChange ||
|
allSettingsIncludesUnitChange ||
|
||||||
resetSettingsIncludesUnitChange
|
resetSettingsIncludesUnitChange
|
||||||
) {
|
) {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { LspWorkerEventType } from '@kittycad/codemirror-lsp-client'
|
import { LspWorkerEventType } from '@kittycad/codemirror-lsp-client'
|
||||||
|
|
||||||
|
import { UnitLength } from 'wasm-lib/kcl/bindings/UnitLength'
|
||||||
|
|
||||||
export enum LspWorker {
|
export enum LspWorker {
|
||||||
Kcl = 'kcl',
|
Kcl = 'kcl',
|
||||||
Copilot = 'copilot',
|
Copilot = 'copilot',
|
||||||
@ -7,6 +9,7 @@ export enum LspWorker {
|
|||||||
export interface KclWorkerOptions {
|
export interface KclWorkerOptions {
|
||||||
wasmUrl: string
|
wasmUrl: string
|
||||||
token: string
|
token: string
|
||||||
|
baseUnit: UnitLength
|
||||||
apiBaseUrl: string
|
apiBaseUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
KclWorkerOptions,
|
KclWorkerOptions,
|
||||||
CopilotWorkerOptions,
|
CopilotWorkerOptions,
|
||||||
} from 'editor/plugins/lsp/types'
|
} from 'editor/plugins/lsp/types'
|
||||||
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
import { err, reportRejection } from 'lib/trap'
|
import { err, reportRejection } from 'lib/trap'
|
||||||
|
|
||||||
const intoServer: IntoServer = new IntoServer()
|
const intoServer: IntoServer = new IntoServer()
|
||||||
@ -45,12 +46,14 @@ export async function copilotLspRun(
|
|||||||
|
|
||||||
export async function kclLspRun(
|
export async function kclLspRun(
|
||||||
config: ServerConfig,
|
config: ServerConfig,
|
||||||
|
engineCommandManager: EngineCommandManager | null,
|
||||||
token: string,
|
token: string,
|
||||||
|
baseUnit: string,
|
||||||
baseUrl: string
|
baseUrl: string
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
console.log('start kcl lsp')
|
console.log('start kcl lsp')
|
||||||
await kcl_lsp_run(config, null, undefined, token, baseUrl)
|
await kcl_lsp_run(config, engineCommandManager, baseUnit, token, baseUrl)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log('kcl lsp failed', e)
|
console.log('kcl lsp failed', e)
|
||||||
// We can't restart here because a moved value, we should do this another way.
|
// We can't restart here because a moved value, we should do this another way.
|
||||||
@ -79,7 +82,13 @@ onmessage = function (event: MessageEvent) {
|
|||||||
switch (worker) {
|
switch (worker) {
|
||||||
case LspWorker.Kcl:
|
case LspWorker.Kcl:
|
||||||
const kclData = eventData as KclWorkerOptions
|
const kclData = eventData as KclWorkerOptions
|
||||||
await kclLspRun(config, kclData.token, kclData.apiBaseUrl)
|
await kclLspRun(
|
||||||
|
config,
|
||||||
|
null,
|
||||||
|
kclData.token,
|
||||||
|
kclData.baseUnit,
|
||||||
|
kclData.apiBaseUrl
|
||||||
|
)
|
||||||
break
|
break
|
||||||
case LspWorker.Copilot:
|
case LspWorker.Copilot:
|
||||||
let copilotData = eventData as CopilotWorkerOptions
|
let copilotData = eventData as CopilotWorkerOptions
|
||||||
|
@ -2,7 +2,7 @@ import { useLayoutEffect, useEffect, useRef } from 'react'
|
|||||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||||
import { deferExecution } from 'lib/utils'
|
import { deferExecution } from 'lib/utils'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { makeDefaultPlanes } from 'lang/wasm'
|
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||||
import { useAppState, useAppStream } from 'AppState'
|
import { useAppState, useAppStream } from 'AppState'
|
||||||
@ -56,6 +56,9 @@ export function useSetupEngineManager(
|
|||||||
makeDefaultPlanes: () => {
|
makeDefaultPlanes: () => {
|
||||||
return makeDefaultPlanes(kclManager.engineCommandManager)
|
return makeDefaultPlanes(kclManager.engineCommandManager)
|
||||||
},
|
},
|
||||||
|
modifyGrid: (hidden: boolean) => {
|
||||||
|
return modifyGrid(kclManager.engineCommandManager, hidden)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
hasSetNonZeroDimensions.current = true
|
hasSetNonZeroDimensions.current = true
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ beforeAll(async () => {
|
|||||||
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
setIsStreamReady: () => {},
|
setIsStreamReady: () => {},
|
||||||
|
modifyGrid: async () => {},
|
||||||
callbackOnEngineLiteConnect: () => {
|
callbackOnEngineLiteConnect: () => {
|
||||||
resolve(true)
|
resolve(true)
|
||||||
},
|
},
|
||||||
|
@ -139,6 +139,7 @@ beforeAll(async () => {
|
|||||||
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
setIsStreamReady: () => {},
|
setIsStreamReady: () => {},
|
||||||
|
modifyGrid: async () => {},
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
callbackOnEngineLiteConnect: async () => {
|
callbackOnEngineLiteConnect: async () => {
|
||||||
const cacheEntries = Object.entries(codeToWriteCacheFor) as [
|
const cacheEntries = Object.entries(codeToWriteCacheFor) as [
|
||||||
|
@ -1399,6 +1399,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private makeDefaultPlanes: () => Promise<DefaultPlanes> | null = () => null
|
private makeDefaultPlanes: () => Promise<DefaultPlanes> | null = () => null
|
||||||
|
private modifyGrid: (hidden: boolean) => Promise<void> | null = () => null
|
||||||
|
|
||||||
private onEngineConnectionOpened = () => {}
|
private onEngineConnectionOpened = () => {}
|
||||||
private onEngineConnectionClosed = () => {}
|
private onEngineConnectionClosed = () => {}
|
||||||
@ -1431,6 +1432,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
height,
|
height,
|
||||||
token,
|
token,
|
||||||
makeDefaultPlanes,
|
makeDefaultPlanes,
|
||||||
|
modifyGrid,
|
||||||
settings = {
|
settings = {
|
||||||
pool: null,
|
pool: null,
|
||||||
theme: Themes.Dark,
|
theme: Themes.Dark,
|
||||||
@ -1450,12 +1452,14 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
height: number
|
height: number
|
||||||
token?: string
|
token?: string
|
||||||
makeDefaultPlanes: () => Promise<DefaultPlanes>
|
makeDefaultPlanes: () => Promise<DefaultPlanes>
|
||||||
|
modifyGrid: (hidden: boolean) => Promise<void>
|
||||||
settings?: SettingsViaQueryString
|
settings?: SettingsViaQueryString
|
||||||
}) {
|
}) {
|
||||||
if (settings) {
|
if (settings) {
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
}
|
}
|
||||||
this.makeDefaultPlanes = makeDefaultPlanes
|
this.makeDefaultPlanes = makeDefaultPlanes
|
||||||
|
this.modifyGrid = modifyGrid
|
||||||
if (width === 0 || height === 0) {
|
if (width === 0 || height === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1535,6 +1539,11 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
type: 'default_camera_get_settings',
|
type: 'default_camera_get_settings',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
// We want modify the grid first because we don't want it to flash.
|
||||||
|
// Ideally these would already be default hidden in engine (TODO do
|
||||||
|
// that) https://github.com/KittyCAD/engine/issues/2282
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
this.modifyGrid(!this.settings.showScaleGrid)?.then(async () => {
|
||||||
await this.initPlanes()
|
await this.initPlanes()
|
||||||
setIsStreamReady(true)
|
setIsStreamReady(true)
|
||||||
|
|
||||||
@ -1544,6 +1553,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
detail: this.engineConnection,
|
detail: this.engineConnection,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.engineConnection.addEventListener(
|
this.engineConnection.addEventListener(
|
||||||
@ -2202,6 +2212,15 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}).catch(reportRejection)
|
}).catch(reportRejection)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the visibility of the scale grid in the engine scene.
|
||||||
|
* @param visible - whether to show or hide the scale grid
|
||||||
|
*/
|
||||||
|
setScaleGridVisibility(visible: boolean) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
this.modifyGrid(!visible)
|
||||||
|
}
|
||||||
|
|
||||||
// Some "objects" have the same source range, such as sketch_mode_start and start_path.
|
// Some "objects" have the same source range, such as sketch_mode_start and start_path.
|
||||||
// So when passing a range, we need to also specify the command type
|
// So when passing a range, we need to also specify the command type
|
||||||
mapRangeToObjectId(
|
mapRangeToObjectId(
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import init, {
|
import init, {
|
||||||
parse_wasm,
|
parse_wasm,
|
||||||
recast_wasm,
|
recast_wasm,
|
||||||
execute,
|
execute_wasm,
|
||||||
kcl_lint,
|
kcl_lint,
|
||||||
modify_ast_for_sketch_wasm,
|
modify_ast_for_sketch_wasm,
|
||||||
is_points_ccw,
|
is_points_ccw,
|
||||||
get_tangential_arc_to_info,
|
get_tangential_arc_to_info,
|
||||||
program_memory_init,
|
program_memory_init,
|
||||||
make_default_planes,
|
make_default_planes,
|
||||||
|
modify_grid,
|
||||||
coredump,
|
coredump,
|
||||||
toml_stringify,
|
toml_stringify,
|
||||||
default_app_settings,
|
default_app_settings,
|
||||||
@ -42,9 +43,7 @@ import { Environment } from '../wasm-lib/kcl/bindings/Environment'
|
|||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
||||||
import { SourceRange as RustSourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
import { SourceRange as RustSourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
||||||
import { getChangedSettingsAtLevel } from 'lib/settings/settingsUtils'
|
|
||||||
|
|
||||||
export type { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
|
||||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||||
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
||||||
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
|
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
|
||||||
@ -494,20 +493,18 @@ export const _executor = async (
|
|||||||
return Promise.reject(programMemoryOverride)
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let jsAppSettings = default_app_settings()
|
let baseUnit = 'mm'
|
||||||
if (!TEST) {
|
if (!TEST) {
|
||||||
const getSettingsState = import('components/SettingsAuthProvider').then(
|
const getSettingsState = import('components/SettingsAuthProvider').then(
|
||||||
(module) => module.getSettingsState
|
(module) => module.getSettingsState
|
||||||
)
|
)
|
||||||
const settings = (await getSettingsState)()
|
baseUnit =
|
||||||
if (settings) {
|
(await getSettingsState)()?.modeling.defaultUnit.current || 'mm'
|
||||||
jsAppSettings = getChangedSettingsAtLevel(settings, 'user')
|
|
||||||
}
|
}
|
||||||
}
|
const execState: RawExecState = await execute_wasm(
|
||||||
const execState: RawExecState = await execute(
|
|
||||||
JSON.stringify(node),
|
JSON.stringify(node),
|
||||||
JSON.stringify(programMemoryOverride?.toRaw() || null),
|
JSON.stringify(programMemoryOverride?.toRaw() || null),
|
||||||
JSON.stringify({ settings: jsAppSettings }),
|
baseUnit,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
fileSystemManager
|
fileSystemManager
|
||||||
)
|
)
|
||||||
@ -555,6 +552,20 @@ export const makeDefaultPlanes = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const modifyGrid = async (
|
||||||
|
engineCommandManager: EngineCommandManager,
|
||||||
|
hidden: boolean
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await modify_grid(engineCommandManager, hidden)
|
||||||
|
return
|
||||||
|
} catch (e) {
|
||||||
|
// TODO: do something real with the error.
|
||||||
|
console.log('modify grid error', e)
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const modifyAstForSketch = async (
|
export const modifyAstForSketch = async (
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
|
@ -112,6 +112,9 @@ export async function executor(
|
|||||||
makeDefaultPlanes: () => {
|
makeDefaultPlanes: () => {
|
||||||
return new Promise((resolve) => resolve(defaultPlanes))
|
return new Promise((resolve) => resolve(defaultPlanes))
|
||||||
},
|
},
|
||||||
|
modifyGrid: (hidden: boolean) => {
|
||||||
|
return new Promise((resolve) => resolve())
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
@ -42,6 +42,8 @@ export const settingsMachine = setup({
|
|||||||
setClientTheme: () => {},
|
setClientTheme: () => {},
|
||||||
'Execute AST': () => {},
|
'Execute AST': () => {},
|
||||||
toastSuccess: () => {},
|
toastSuccess: () => {},
|
||||||
|
setEngineEdges: () => {},
|
||||||
|
setEngineScaleGridVisibility: () => {},
|
||||||
setClientSideSceneUnits: () => {},
|
setClientSideSceneUnits: () => {},
|
||||||
persistSettings: () => {},
|
persistSettings: () => {},
|
||||||
resetSettings: assign(({ context, event }) => {
|
resetSettings: assign(({ context, event }) => {
|
||||||
@ -170,7 +172,7 @@ export const settingsMachine = setup({
|
|||||||
'set.modeling.highlightEdges': {
|
'set.modeling.highlightEdges': {
|
||||||
target: 'persisting settings',
|
target: 'persisting settings',
|
||||||
|
|
||||||
actions: ['setSettingAtLevel', 'toastSuccess', 'Execute AST'],
|
actions: ['setSettingAtLevel', 'toastSuccess', 'setEngineEdges'],
|
||||||
},
|
},
|
||||||
|
|
||||||
'Reset settings': {
|
'Reset settings': {
|
||||||
@ -199,7 +201,11 @@ export const settingsMachine = setup({
|
|||||||
|
|
||||||
'set.modeling.showScaleGrid': {
|
'set.modeling.showScaleGrid': {
|
||||||
target: 'persisting settings',
|
target: 'persisting settings',
|
||||||
actions: ['setSettingAtLevel', 'toastSuccess', 'Execute AST'],
|
actions: [
|
||||||
|
'setSettingAtLevel',
|
||||||
|
'toastSuccess',
|
||||||
|
'setEngineScaleGridVisibility',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -120,61 +120,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the visibility of edges.
|
|
||||||
async fn set_edge_visibility(
|
|
||||||
&self,
|
|
||||||
visible: bool,
|
|
||||||
source_range: SourceRange,
|
|
||||||
) -> Result<(), crate::errors::KclError> {
|
|
||||||
self.batch_modeling_cmd(
|
|
||||||
uuid::Uuid::new_v4(),
|
|
||||||
source_range,
|
|
||||||
&ModelingCmd::from(mcmd::EdgeLinesVisible { hidden: !visible }),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_units(
|
|
||||||
&self,
|
|
||||||
units: crate::UnitLength,
|
|
||||||
source_range: SourceRange,
|
|
||||||
) -> Result<(), crate::errors::KclError> {
|
|
||||||
// Before we even start executing the program, set the units.
|
|
||||||
self.batch_modeling_cmd(
|
|
||||||
uuid::Uuid::new_v4(),
|
|
||||||
source_range,
|
|
||||||
&ModelingCmd::from(mcmd::SetSceneUnits { unit: units.into() }),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Re-run the command to apply the settings.
|
|
||||||
async fn reapply_settings(
|
|
||||||
&self,
|
|
||||||
settings: &crate::ExecutorSettings,
|
|
||||||
source_range: SourceRange,
|
|
||||||
) -> Result<(), crate::errors::KclError> {
|
|
||||||
// Set the edge visibility.
|
|
||||||
self.set_edge_visibility(settings.highlight_edges, source_range).await?;
|
|
||||||
|
|
||||||
// Change the units.
|
|
||||||
self.set_units(settings.units, source_range).await?;
|
|
||||||
|
|
||||||
// Send the command to show the grid.
|
|
||||||
self.modify_grid(!settings.show_grid, source_range).await?;
|
|
||||||
|
|
||||||
// We do not have commands for changing ssao on the fly.
|
|
||||||
|
|
||||||
// Flush the batch queue, so the settings are applied right away.
|
|
||||||
self.flush_batch(false, source_range).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a modeling command to the batch but don't fire it right away.
|
// Add a modeling command to the batch but don't fire it right away.
|
||||||
async fn batch_modeling_cmd(
|
async fn batch_modeling_cmd(
|
||||||
&self,
|
&self,
|
||||||
@ -559,11 +504,11 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn modify_grid(&self, hidden: bool, source_range: SourceRange) -> Result<(), KclError> {
|
async fn modify_grid(&self, hidden: bool) -> Result<(), KclError> {
|
||||||
// Hide/show the grid.
|
// Hide/show the grid.
|
||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
uuid::Uuid::new_v4(),
|
||||||
source_range,
|
Default::default(),
|
||||||
&ModelingCmd::from(mcmd::ObjectVisible {
|
&ModelingCmd::from(mcmd::ObjectVisible {
|
||||||
hidden,
|
hidden,
|
||||||
object_id: *GRID_OBJECT_ID,
|
object_id: *GRID_OBJECT_ID,
|
||||||
@ -574,7 +519,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
// Hide/show the grid scale text.
|
// Hide/show the grid scale text.
|
||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
uuid::Uuid::new_v4(),
|
||||||
source_range,
|
Default::default(),
|
||||||
&ModelingCmd::from(mcmd::ObjectVisible {
|
&ModelingCmd::from(mcmd::ObjectVisible {
|
||||||
hidden,
|
hidden,
|
||||||
object_id: *GRID_SCALE_TEXT_OBJECT_ID,
|
object_id: *GRID_SCALE_TEXT_OBJECT_ID,
|
||||||
@ -582,6 +527,8 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
self.flush_batch(false, Default::default()).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
//! Functions for helping with caching an ast and finding the parts the changed.
|
|
||||||
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
execution::ExecState,
|
|
||||||
parsing::ast::types::{Node, Program},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Information for the caching an AST and smartly re-executing it if we can.
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
||||||
#[ts(export)]
|
|
||||||
pub struct CacheInformation {
|
|
||||||
/// The old information.
|
|
||||||
pub old: Option<OldAstState>,
|
|
||||||
/// The new ast to executed.
|
|
||||||
pub new_ast: Node<Program>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The old ast and program memory.
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
||||||
#[ts(export)]
|
|
||||||
pub struct OldAstState {
|
|
||||||
/// The ast.
|
|
||||||
pub ast: Node<Program>,
|
|
||||||
/// The exec state.
|
|
||||||
pub exec_state: ExecState,
|
|
||||||
/// The last settings used for execution.
|
|
||||||
pub settings: crate::execution::ExecutorSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<crate::Program> for CacheInformation {
|
|
||||||
fn from(program: crate::Program) -> Self {
|
|
||||||
CacheInformation {
|
|
||||||
old: None,
|
|
||||||
new_ast: program.ast,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The result of a cache check.
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
||||||
#[ts(export)]
|
|
||||||
pub struct CacheResult {
|
|
||||||
/// Should we clear the scene and start over?
|
|
||||||
pub clear_scene: bool,
|
|
||||||
/// The program that needs to be executed.
|
|
||||||
pub program: Node<Program>,
|
|
||||||
}
|
|
@ -23,25 +23,26 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
|||||||
pub use function_param::FunctionParam;
|
pub use function_param::FunctionParam;
|
||||||
pub use kcl_value::{KclObjectFields, KclValue};
|
pub use kcl_value::{KclObjectFields, KclValue};
|
||||||
|
|
||||||
pub(crate) mod cache;
|
|
||||||
mod exec_ast;
|
|
||||||
mod function_param;
|
|
||||||
mod kcl_value;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::{EngineManager, ExecutionKind},
|
engine::{EngineManager, ExecutionKind},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::cache::{CacheInformation, CacheResult},
|
|
||||||
fs::{FileManager, FileSystem},
|
fs::{FileManager, FileSystem},
|
||||||
parsing::ast::types::{
|
parsing::ast::{
|
||||||
|
cache::{get_changed_program, CacheInformation},
|
||||||
|
types::{
|
||||||
BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, TagDeclarator, TagNode,
|
BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, TagDeclarator, TagNode,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
settings::types::UnitLength,
|
settings::types::UnitLength,
|
||||||
source_range::{ModuleId, SourceRange},
|
source_range::{ModuleId, SourceRange},
|
||||||
std::{args::Arg, StdLib},
|
std::{args::Arg, StdLib},
|
||||||
ExecError, Program,
|
ExecError, Program,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod exec_ast;
|
||||||
|
mod function_param;
|
||||||
|
mod kcl_value;
|
||||||
|
|
||||||
/// State for executing a program.
|
/// State for executing a program.
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -1659,6 +1660,17 @@ impl ExecutorContext {
|
|||||||
let engine: Arc<Box<dyn EngineManager>> =
|
let engine: Arc<Box<dyn EngineManager>> =
|
||||||
Arc::new(Box::new(crate::engine::conn::EngineConnection::new(ws).await?));
|
Arc::new(Box::new(crate::engine::conn::EngineConnection::new(ws).await?));
|
||||||
|
|
||||||
|
// Set the edge visibility.
|
||||||
|
engine
|
||||||
|
.batch_modeling_cmd(
|
||||||
|
uuid::Uuid::new_v4(),
|
||||||
|
SourceRange::default(),
|
||||||
|
&ModelingCmd::from(mcmd::EdgeLinesVisible {
|
||||||
|
hidden: !settings.highlight_edges,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
engine,
|
engine,
|
||||||
fs: Arc::new(FileManager::new()),
|
fs: Arc::new(FileManager::new()),
|
||||||
@ -1685,7 +1697,7 @@ impl ExecutorContext {
|
|||||||
pub async fn new(
|
pub async fn new(
|
||||||
engine_manager: crate::engine::conn_wasm::EngineCommandManager,
|
engine_manager: crate::engine::conn_wasm::EngineCommandManager,
|
||||||
fs_manager: crate::fs::wasm::FileSystemManager,
|
fs_manager: crate::fs::wasm::FileSystemManager,
|
||||||
settings: ExecutorSettings,
|
units: UnitLength,
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
Ok(ExecutorContext {
|
Ok(ExecutorContext {
|
||||||
engine: Arc::new(Box::new(
|
engine: Arc::new(Box::new(
|
||||||
@ -1695,16 +1707,16 @@ impl ExecutorContext {
|
|||||||
)),
|
)),
|
||||||
fs: Arc::new(FileManager::new(fs_manager)),
|
fs: Arc::new(FileManager::new(fs_manager)),
|
||||||
stdlib: Arc::new(StdLib::new()),
|
stdlib: Arc::new(StdLib::new()),
|
||||||
settings,
|
settings: ExecutorSettings {
|
||||||
|
units,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
context_type: ContextType::Live,
|
context_type: ContextType::Live,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub async fn new_mock(
|
pub async fn new_mock(fs_manager: crate::fs::wasm::FileSystemManager, units: UnitLength) -> Result<Self, String> {
|
||||||
fs_manager: crate::fs::wasm::FileSystemManager,
|
|
||||||
settings: ExecutorSettings,
|
|
||||||
) -> Result<Self, String> {
|
|
||||||
Ok(ExecutorContext {
|
Ok(ExecutorContext {
|
||||||
engine: Arc::new(Box::new(
|
engine: Arc::new(Box::new(
|
||||||
crate::engine::conn_mock::EngineConnection::new()
|
crate::engine::conn_mock::EngineConnection::new()
|
||||||
@ -1713,7 +1725,10 @@ impl ExecutorContext {
|
|||||||
)),
|
)),
|
||||||
fs: Arc::new(FileManager::new(fs_manager)),
|
fs: Arc::new(FileManager::new(fs_manager)),
|
||||||
stdlib: Arc::new(StdLib::new()),
|
stdlib: Arc::new(StdLib::new()),
|
||||||
settings,
|
settings: ExecutorSettings {
|
||||||
|
units,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
context_type: ContextType::Mock,
|
context_type: ContextType::Mock,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1802,71 +1817,6 @@ impl ExecutorContext {
|
|||||||
// AND if we aren't in wasm it doesn't really matter.
|
// AND if we aren't in wasm it doesn't really matter.
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// Given an old ast, old program memory and new ast, find the parts of the code that need to be
|
|
||||||
// re-executed.
|
|
||||||
// This function should never error, because in the case of any internal error, we should just pop
|
|
||||||
// the cache.
|
|
||||||
pub async fn get_changed_program(&self, info: CacheInformation) -> Option<CacheResult> {
|
|
||||||
let Some(old) = info.old else {
|
|
||||||
// We have no old info, we need to re-execute the whole thing.
|
|
||||||
return Some(CacheResult {
|
|
||||||
clear_scene: true,
|
|
||||||
program: info.new_ast,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the settings are different we might need to bust the cache.
|
|
||||||
// We specifically do this before checking if they are the exact same.
|
|
||||||
if old.settings != self.settings {
|
|
||||||
// If the units are different we need to re-execute the whole thing.
|
|
||||||
if old.settings.units != self.settings.units {
|
|
||||||
return Some(CacheResult {
|
|
||||||
clear_scene: true,
|
|
||||||
program: info.new_ast,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If anything else is different we do not need to re-execute, but rather just
|
|
||||||
// run the settings again.
|
|
||||||
|
|
||||||
if self
|
|
||||||
.engine
|
|
||||||
.reapply_settings(&self.settings, Default::default())
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
// Bust the cache, we errored.
|
|
||||||
return Some(CacheResult {
|
|
||||||
clear_scene: true,
|
|
||||||
program: info.new_ast,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the ASTs are the EXACT same we return None.
|
|
||||||
// We don't even need to waste time computing the digests.
|
|
||||||
if old.ast == info.new_ast {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut old_ast = old.ast.inner;
|
|
||||||
old_ast.compute_digest();
|
|
||||||
let mut new_ast = info.new_ast.inner.clone();
|
|
||||||
new_ast.compute_digest();
|
|
||||||
|
|
||||||
// Check if the digest is the same.
|
|
||||||
if old_ast.digest == new_ast.digest {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the changes were only to Non-code areas, like comments or whitespace.
|
|
||||||
|
|
||||||
// For any unhandled cases just re-execute the whole thing.
|
|
||||||
Some(CacheResult {
|
|
||||||
clear_scene: true,
|
|
||||||
program: info.new_ast,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform the execution of a program.
|
/// Perform the execution of a program.
|
||||||
/// You can optionally pass in some initialization memory.
|
/// You can optionally pass in some initialization memory.
|
||||||
@ -1887,7 +1837,7 @@ impl ExecutorContext {
|
|||||||
let _stats = crate::log::LogPerfStats::new("Interpretation");
|
let _stats = crate::log::LogPerfStats::new("Interpretation");
|
||||||
|
|
||||||
// Get the program that actually changed from the old and new information.
|
// Get the program that actually changed from the old and new information.
|
||||||
let cache_result = self.get_changed_program(cache_info.clone()).await;
|
let cache_result = get_changed_program(cache_info.clone(), &self.settings);
|
||||||
|
|
||||||
// Check if we don't need to re-execute.
|
// Check if we don't need to re-execute.
|
||||||
let Some(cache_result) = cache_result else {
|
let Some(cache_result) = cache_result else {
|
||||||
@ -1904,9 +1854,23 @@ impl ExecutorContext {
|
|||||||
|
|
||||||
// TODO: Use the top-level file's path.
|
// TODO: Use the top-level file's path.
|
||||||
exec_state.add_module(std::path::PathBuf::from(""));
|
exec_state.add_module(std::path::PathBuf::from(""));
|
||||||
|
// Before we even start executing the program, set the units.
|
||||||
// Re-apply the settings, in case the cache was busted.
|
self.engine
|
||||||
self.engine.reapply_settings(&self.settings, Default::default()).await?;
|
.batch_modeling_cmd(
|
||||||
|
exec_state.id_generator.next_uuid(),
|
||||||
|
SourceRange::default(),
|
||||||
|
&ModelingCmd::from(mcmd::SetSceneUnits {
|
||||||
|
unit: match self.settings.units {
|
||||||
|
UnitLength::Cm => kcmc::units::UnitLength::Centimeters,
|
||||||
|
UnitLength::Ft => kcmc::units::UnitLength::Feet,
|
||||||
|
UnitLength::In => kcmc::units::UnitLength::Inches,
|
||||||
|
UnitLength::M => kcmc::units::UnitLength::Meters,
|
||||||
|
UnitLength::Mm => kcmc::units::UnitLength::Millimeters,
|
||||||
|
UnitLength::Yd => kcmc::units::UnitLength::Yards,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
self.inner_execute(&cache_result.program, exec_state, crate::execution::BodyType::Root)
|
self.inner_execute(&cache_result.program, exec_state, crate::execution::BodyType::Root)
|
||||||
.await?;
|
.await?;
|
||||||
@ -2183,8 +2147,23 @@ impl ExecutorContext {
|
|||||||
self.settings.units = units;
|
self.settings.units = units;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a snapshot of the current scene.
|
/// Execute the program, then get a PNG screenshot.
|
||||||
pub async fn prepare_snapshot(&self) -> std::result::Result<TakeSnapshot, ExecError> {
|
pub async fn execute_and_prepare_snapshot(
|
||||||
|
&self,
|
||||||
|
program: &Program,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
|
) -> std::result::Result<TakeSnapshot, ExecError> {
|
||||||
|
self.execute_and_prepare(program, exec_state).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute the program, return the interpreter and outputs.
|
||||||
|
pub async fn execute_and_prepare(
|
||||||
|
&self,
|
||||||
|
program: &Program,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
|
) -> std::result::Result<TakeSnapshot, ExecError> {
|
||||||
|
self.run(program.clone().into(), exec_state).await?;
|
||||||
|
|
||||||
// Zoom to fit.
|
// Zoom to fit.
|
||||||
self.engine
|
self.engine
|
||||||
.send_modeling_cmd(
|
.send_modeling_cmd(
|
||||||
@ -2220,17 +2199,6 @@ impl ExecutorContext {
|
|||||||
};
|
};
|
||||||
Ok(contents)
|
Ok(contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the program, then get a PNG screenshot.
|
|
||||||
pub async fn execute_and_prepare_snapshot(
|
|
||||||
&self,
|
|
||||||
program: &Program,
|
|
||||||
exec_state: &mut ExecState,
|
|
||||||
) -> std::result::Result<TakeSnapshot, ExecError> {
|
|
||||||
self.run(program.clone().into(), exec_state).await?;
|
|
||||||
|
|
||||||
self.prepare_snapshot().await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For each argument given,
|
/// For each argument given,
|
||||||
@ -2410,12 +2378,9 @@ mod tests {
|
|||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::parsing::ast::types::{DefaultParamVal, Identifier, Node, Parameter};
|
||||||
parsing::ast::types::{DefaultParamVal, Identifier, Node, Parameter},
|
|
||||||
OldAstState,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub async fn parse_execute(code: &str) -> Result<(Program, ExecutorContext, ExecState)> {
|
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
||||||
let program = Program::parse_no_errs(code)?;
|
let program = Program::parse_no_errs(code)?;
|
||||||
|
|
||||||
let ctx = ExecutorContext {
|
let ctx = ExecutorContext {
|
||||||
@ -2426,9 +2391,9 @@ mod tests {
|
|||||||
context_type: ContextType::Mock,
|
context_type: ContextType::Mock,
|
||||||
};
|
};
|
||||||
let mut exec_state = ExecState::default();
|
let mut exec_state = ExecState::default();
|
||||||
ctx.run(program.clone().into(), &mut exec_state).await?;
|
ctx.run(program.into(), &mut exec_state).await?;
|
||||||
|
|
||||||
Ok((program, ctx, exec_state))
|
Ok(exec_state.memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function to get a JSON value from memory and unwrap.
|
/// Convenience function to get a JSON value from memory and unwrap.
|
||||||
@ -2839,39 +2804,36 @@ let shape = layer() |> patternTransform(10, transform, %)
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_math_execute_with_functions() {
|
async fn test_math_execute_with_functions() {
|
||||||
let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#;
|
let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#;
|
||||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
let memory = parse_execute(ast).await.unwrap();
|
||||||
assert_eq!(5.0, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
assert_eq!(5.0, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_math_execute() {
|
async fn test_math_execute() {
|
||||||
let ast = r#"const myVar = 1 + 2 * (3 - 4) / -5 + 6"#;
|
let ast = r#"const myVar = 1 + 2 * (3 - 4) / -5 + 6"#;
|
||||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
let memory = parse_execute(ast).await.unwrap();
|
||||||
assert_eq!(7.4, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
assert_eq!(7.4, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_math_execute_start_negative() {
|
async fn test_math_execute_start_negative() {
|
||||||
let ast = r#"const myVar = -5 + 6"#;
|
let ast = r#"const myVar = -5 + 6"#;
|
||||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
let memory = parse_execute(ast).await.unwrap();
|
||||||
assert_eq!(1.0, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
assert_eq!(1.0, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_math_execute_with_pi() {
|
async fn test_math_execute_with_pi() {
|
||||||
let ast = r#"const myVar = pi() * 2"#;
|
let ast = r#"const myVar = pi() * 2"#;
|
||||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
let memory = parse_execute(ast).await.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(std::f64::consts::TAU, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||||
std::f64::consts::TAU,
|
|
||||||
mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_math_define_decimal_without_leading_zero() {
|
async fn test_math_define_decimal_without_leading_zero() {
|
||||||
let ast = r#"let thing = .4 + 7"#;
|
let ast = r#"let thing = .4 + 7"#;
|
||||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
let memory = parse_execute(ast).await.unwrap();
|
||||||
assert_eq!(7.4, mem_get_json(&exec_state.memory, "thing").as_f64().unwrap());
|
assert_eq!(7.4, mem_get_json(&memory, "thing").as_f64().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2910,11 +2872,11 @@ fn check = (x) => {
|
|||||||
}
|
}
|
||||||
check(false)
|
check(false)
|
||||||
"#;
|
"#;
|
||||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
let mem = parse_execute(ast).await.unwrap();
|
||||||
assert_eq!(false, mem_get_json(&exec_state.memory, "notTrue").as_bool().unwrap());
|
assert_eq!(false, mem_get_json(&mem, "notTrue").as_bool().unwrap());
|
||||||
assert_eq!(true, mem_get_json(&exec_state.memory, "notFalse").as_bool().unwrap());
|
assert_eq!(true, mem_get_json(&mem, "notFalse").as_bool().unwrap());
|
||||||
assert_eq!(true, mem_get_json(&exec_state.memory, "c").as_bool().unwrap());
|
assert_eq!(true, mem_get_json(&mem, "c").as_bool().unwrap());
|
||||||
assert_eq!(false, mem_get_json(&exec_state.memory, "d").as_bool().unwrap());
|
assert_eq!(false, mem_get_json(&mem, "d").as_bool().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -3296,310 +3258,4 @@ let w = f() + f()
|
|||||||
let json = serde_json::to_string(&mem).unwrap();
|
let json = serde_json::to_string(&mem).unwrap();
|
||||||
assert_eq!(json, r#"{"type":"Solids","value":[]}"#);
|
assert_eq!(json, r#"{"type":"Solids","value":[]}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Easy case where we have no old ast and memory.
|
|
||||||
// We need to re-execute everything.
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_get_changed_program_no_old_information() {
|
|
||||||
let new = r#"// Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
|
||||||
let (program, ctx, _) = parse_execute(new).await.unwrap();
|
|
||||||
|
|
||||||
let result = ctx
|
|
||||||
.get_changed_program(CacheInformation {
|
|
||||||
old: None,
|
|
||||||
new_ast: program.ast.clone(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert!(result.is_some());
|
|
||||||
|
|
||||||
let result = result.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(result.program, program.ast);
|
|
||||||
assert!(result.clear_scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_get_changed_program_same_code() {
|
|
||||||
let new = r#"// Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
|
||||||
|
|
||||||
let (program, ctx, exec_state) = parse_execute(new).await.unwrap();
|
|
||||||
|
|
||||||
let result = ctx
|
|
||||||
.get_changed_program(CacheInformation {
|
|
||||||
old: Some(OldAstState {
|
|
||||||
ast: program.ast.clone(),
|
|
||||||
exec_state,
|
|
||||||
settings: Default::default(),
|
|
||||||
}),
|
|
||||||
new_ast: program.ast.clone(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert_eq!(result, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_get_changed_program_same_code_changed_whitespace() {
|
|
||||||
let old = r#" // Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
|
||||||
|
|
||||||
let new = r#"// Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
|
||||||
|
|
||||||
let (program_old, ctx, exec_state) = parse_execute(old).await.unwrap();
|
|
||||||
|
|
||||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
|
||||||
|
|
||||||
let result = ctx
|
|
||||||
.get_changed_program(CacheInformation {
|
|
||||||
old: Some(OldAstState {
|
|
||||||
ast: program_old.ast.clone(),
|
|
||||||
exec_state,
|
|
||||||
settings: Default::default(),
|
|
||||||
}),
|
|
||||||
new_ast: program_new.ast.clone(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert_eq!(result, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_get_changed_program_same_code_changed_code_comment_start_of_program() {
|
|
||||||
let old = r#" // Removed the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
|
||||||
|
|
||||||
let new = r#"// Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
|
||||||
|
|
||||||
let (program, ctx, exec_state) = parse_execute(old).await.unwrap();
|
|
||||||
|
|
||||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
|
||||||
|
|
||||||
let result = ctx
|
|
||||||
.get_changed_program(CacheInformation {
|
|
||||||
old: Some(OldAstState {
|
|
||||||
ast: program.ast.clone(),
|
|
||||||
exec_state,
|
|
||||||
settings: Default::default(),
|
|
||||||
}),
|
|
||||||
new_ast: program_new.ast.clone(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert_eq!(result, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_get_changed_program_same_code_changed_code_comments() {
|
|
||||||
let old = r#" // Removed the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %) // my thing
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
|
||||||
|
|
||||||
let new = r#"// Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
|
||||||
|
|
||||||
let (program, ctx, exec_state) = parse_execute(old).await.unwrap();
|
|
||||||
|
|
||||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
|
||||||
|
|
||||||
let result = ctx
|
|
||||||
.get_changed_program(CacheInformation {
|
|
||||||
old: Some(OldAstState {
|
|
||||||
ast: program.ast.clone(),
|
|
||||||
exec_state,
|
|
||||||
settings: Default::default(),
|
|
||||||
}),
|
|
||||||
new_ast: program_new.ast.clone(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert!(result.is_some());
|
|
||||||
|
|
||||||
let result = result.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(result.program, program_new.ast);
|
|
||||||
assert!(result.clear_scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Changing the units with the exact same file should bust the cache.
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_get_changed_program_same_code_but_different_units() {
|
|
||||||
let new = r#"// Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
|
||||||
|
|
||||||
let (program, mut ctx, exec_state) = parse_execute(new).await.unwrap();
|
|
||||||
|
|
||||||
// Change the settings to cm.
|
|
||||||
ctx.settings.units = crate::UnitLength::Cm;
|
|
||||||
|
|
||||||
let result = ctx
|
|
||||||
.get_changed_program(CacheInformation {
|
|
||||||
old: Some(OldAstState {
|
|
||||||
ast: program.ast.clone(),
|
|
||||||
exec_state,
|
|
||||||
settings: Default::default(),
|
|
||||||
}),
|
|
||||||
new_ast: program.ast.clone(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert!(result.is_some());
|
|
||||||
|
|
||||||
let result = result.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(result.program, program.ast);
|
|
||||||
assert!(result.clear_scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Changing the grid settings with the exact same file should NOT bust the cache.
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_get_changed_program_same_code_but_different_grid_setting() {
|
|
||||||
let new = r#"// Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
|
||||||
|
|
||||||
let (program, mut ctx, exec_state) = parse_execute(new).await.unwrap();
|
|
||||||
|
|
||||||
// Change the settings.
|
|
||||||
ctx.settings.show_grid = !ctx.settings.show_grid;
|
|
||||||
|
|
||||||
let result = ctx
|
|
||||||
.get_changed_program(CacheInformation {
|
|
||||||
old: Some(OldAstState {
|
|
||||||
ast: program.ast.clone(),
|
|
||||||
exec_state,
|
|
||||||
settings: Default::default(),
|
|
||||||
}),
|
|
||||||
new_ast: program.ast.clone(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert_eq!(result, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Changing the edge visibility settings with the exact same file should NOT bust the cache.
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_get_changed_program_same_code_but_different_edge_visiblity_setting() {
|
|
||||||
let new = r#"// Remove the end face for the extrusion.
|
|
||||||
firstSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, 12], %)
|
|
||||||
|> line([24, 0], %)
|
|
||||||
|> line([0, -24], %)
|
|
||||||
|> line([-24, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(6, %)
|
|
||||||
|
|
||||||
// Remove the end face for the extrusion.
|
|
||||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
|
||||||
|
|
||||||
let (program, mut ctx, exec_state) = parse_execute(new).await.unwrap();
|
|
||||||
|
|
||||||
// Change the settings.
|
|
||||||
ctx.settings.highlight_edges = !ctx.settings.highlight_edges;
|
|
||||||
|
|
||||||
let result = ctx
|
|
||||||
.get_changed_program(CacheInformation {
|
|
||||||
old: Some(OldAstState {
|
|
||||||
ast: program.ast.clone(),
|
|
||||||
exec_state,
|
|
||||||
settings: Default::default(),
|
|
||||||
}),
|
|
||||||
new_ast: program.ast.clone(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert_eq!(result, None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -82,15 +82,16 @@ mod wasm;
|
|||||||
pub use coredump::CoreDump;
|
pub use coredump::CoreDump;
|
||||||
pub use engine::{EngineManager, ExecutionKind};
|
pub use engine::{EngineManager, ExecutionKind};
|
||||||
pub use errors::{CompilationError, ConnectionError, ExecError, KclError};
|
pub use errors::{CompilationError, ConnectionError, ExecError, KclError};
|
||||||
pub use execution::{
|
pub use execution::{ExecState, ExecutorContext, ExecutorSettings};
|
||||||
cache::{CacheInformation, OldAstState},
|
|
||||||
ExecState, ExecutorContext, ExecutorSettings,
|
|
||||||
};
|
|
||||||
pub use lsp::{
|
pub use lsp::{
|
||||||
copilot::Backend as CopilotLspBackend,
|
copilot::Backend as CopilotLspBackend,
|
||||||
kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
|
kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
|
||||||
};
|
};
|
||||||
pub use parsing::ast::{modify::modify_ast_for_sketch, types::FormatOptions};
|
pub use parsing::ast::{
|
||||||
|
cache::{CacheInformation, OldAstState},
|
||||||
|
modify::modify_ast_for_sketch,
|
||||||
|
types::FormatOptions,
|
||||||
|
};
|
||||||
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
||||||
pub use source_range::{ModuleId, SourceRange};
|
pub use source_range::{ModuleId, SourceRange};
|
||||||
|
|
||||||
|
@ -45,11 +45,14 @@ use crate::{
|
|||||||
errors::Suggestion,
|
errors::Suggestion,
|
||||||
lsp::{backend::Backend as _, util::IntoDiagnostic},
|
lsp::{backend::Backend as _, util::IntoDiagnostic},
|
||||||
parsing::{
|
parsing::{
|
||||||
ast::types::{Expr, Node, VariableKind},
|
ast::{
|
||||||
|
cache::{CacheInformation, OldAstState},
|
||||||
|
types::{Expr, Node, VariableKind},
|
||||||
|
},
|
||||||
token::TokenStream,
|
token::TokenStream,
|
||||||
PIPE_OPERATOR,
|
PIPE_OPERATOR,
|
||||||
},
|
},
|
||||||
CacheInformation, ModuleId, OldAstState, Program, SourceRange,
|
ModuleId, Program, SourceRange,
|
||||||
};
|
};
|
||||||
const SEMANTIC_TOKEN_TYPES: [SemanticTokenType; 10] = [
|
const SEMANTIC_TOKEN_TYPES: [SemanticTokenType; 10] = [
|
||||||
SemanticTokenType::NUMBER,
|
SemanticTokenType::NUMBER,
|
||||||
|
373
src/wasm-lib/kcl/src/parsing/ast/cache.rs
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
//! Functions for helping with caching an ast and finding the parts the changed.
|
||||||
|
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
execution::ExecState,
|
||||||
|
parsing::ast::types::{Node, Program},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Information for the caching an AST and smartly re-executing it if we can.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct CacheInformation {
|
||||||
|
/// The old information.
|
||||||
|
pub old: Option<OldAstState>,
|
||||||
|
/// The new ast to executed.
|
||||||
|
pub new_ast: Node<Program>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The old ast and program memory.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct OldAstState {
|
||||||
|
/// The ast.
|
||||||
|
pub ast: Node<Program>,
|
||||||
|
/// The exec state.
|
||||||
|
pub exec_state: ExecState,
|
||||||
|
/// The last settings used for execution.
|
||||||
|
pub settings: crate::execution::ExecutorSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::Program> for CacheInformation {
|
||||||
|
fn from(program: crate::Program) -> Self {
|
||||||
|
CacheInformation {
|
||||||
|
old: None,
|
||||||
|
new_ast: program.ast,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of a cache check.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct CacheResult {
|
||||||
|
/// Should we clear the scene and start over?
|
||||||
|
pub clear_scene: bool,
|
||||||
|
/// The program that needs to be executed.
|
||||||
|
pub program: Node<Program>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given an old ast, old program memory and new ast, find the parts of the code that need to be
|
||||||
|
// re-executed.
|
||||||
|
// This function should never error, because in the case of any internal error, we should just pop
|
||||||
|
// the cache.
|
||||||
|
pub fn get_changed_program(
|
||||||
|
info: CacheInformation,
|
||||||
|
new_settings: &crate::execution::ExecutorSettings,
|
||||||
|
) -> Option<CacheResult> {
|
||||||
|
let Some(old) = info.old else {
|
||||||
|
// We have no old info, we need to re-execute the whole thing.
|
||||||
|
return Some(CacheResult {
|
||||||
|
clear_scene: true,
|
||||||
|
program: info.new_ast,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the settings are different we need to bust the cache.
|
||||||
|
// We specifically do this before checking if they are the exact same.
|
||||||
|
if old.settings != *new_settings {
|
||||||
|
return Some(CacheResult {
|
||||||
|
clear_scene: true,
|
||||||
|
program: info.new_ast,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the ASTs are the EXACT same we return None.
|
||||||
|
// We don't even need to waste time computing the digests.
|
||||||
|
if old.ast == info.new_ast {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut old_ast = old.ast.inner;
|
||||||
|
old_ast.compute_digest();
|
||||||
|
let mut new_ast = info.new_ast.inner.clone();
|
||||||
|
new_ast.compute_digest();
|
||||||
|
|
||||||
|
// Check if the digest is the same.
|
||||||
|
if old_ast.digest == new_ast.digest {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the changes were only to Non-code areas, like comments or whitespace.
|
||||||
|
|
||||||
|
// For any unhandled cases just re-execute the whole thing.
|
||||||
|
Some(CacheResult {
|
||||||
|
clear_scene: true,
|
||||||
|
program: info.new_ast,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
async fn execute(program: &crate::Program) -> Result<ExecState> {
|
||||||
|
let ctx = crate::execution::ExecutorContext {
|
||||||
|
engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)),
|
||||||
|
fs: Arc::new(crate::fs::FileManager::new()),
|
||||||
|
stdlib: Arc::new(crate::std::StdLib::new()),
|
||||||
|
settings: Default::default(),
|
||||||
|
context_type: crate::execution::ContextType::Mock,
|
||||||
|
};
|
||||||
|
let mut exec_state = crate::execution::ExecState::default();
|
||||||
|
ctx.run(program.clone().into(), &mut exec_state).await?;
|
||||||
|
|
||||||
|
Ok(exec_state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Easy case where we have no old ast and memory.
|
||||||
|
// We need to re-execute everything.
|
||||||
|
#[test]
|
||||||
|
fn test_get_changed_program_no_old_information() {
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program = crate::Program::parse_no_errs(new).unwrap().ast;
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: None,
|
||||||
|
new_ast: program.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_some());
|
||||||
|
|
||||||
|
let result = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.program, program);
|
||||||
|
assert!(result.clear_scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code() {
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program).await.unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program.ast.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code_changed_whitespace() {
|
||||||
|
let old = r#" // Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||||
|
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program_old).await.unwrap();
|
||||||
|
|
||||||
|
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program_old.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program_new.ast.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code_changed_code_comment_start_of_program() {
|
||||||
|
let old = r#" // Removed the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||||
|
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program_old).await.unwrap();
|
||||||
|
|
||||||
|
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program_old.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program_new.ast.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code_changed_code_comments() {
|
||||||
|
let old = r#" // Removed the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %) // my thing
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||||
|
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program_old).await.unwrap();
|
||||||
|
|
||||||
|
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program_old.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program_new.ast.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_some());
|
||||||
|
|
||||||
|
let result = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.program, program_new.ast);
|
||||||
|
assert!(result.clear_scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changing the units with the exact same file should bust the cache.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code_but_different_units() {
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program).await.unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program.ast.clone(),
|
||||||
|
},
|
||||||
|
&crate::ExecutorSettings {
|
||||||
|
units: crate::UnitLength::Cm,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_some());
|
||||||
|
|
||||||
|
let result = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.program, program.ast);
|
||||||
|
assert!(result.clear_scene);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
pub(crate) mod cache;
|
||||||
pub(crate) mod digest;
|
pub(crate) mod digest;
|
||||||
pub mod modify;
|
pub mod modify;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
@ -55,11 +55,7 @@ async fn do_execute_and_snapshot(
|
|||||||
program: Program,
|
program: Program,
|
||||||
) -> Result<(crate::execution::ExecState, image::DynamicImage), ExecError> {
|
) -> Result<(crate::execution::ExecState, image::DynamicImage), ExecError> {
|
||||||
let mut exec_state = Default::default();
|
let mut exec_state = Default::default();
|
||||||
let snapshot_png_bytes = ctx
|
let snapshot_png_bytes = ctx.execute_and_prepare(&program, &mut exec_state).await?.contents.0;
|
||||||
.execute_and_prepare_snapshot(&program, &mut exec_state)
|
|
||||||
.await?
|
|
||||||
.contents
|
|
||||||
.0;
|
|
||||||
|
|
||||||
// Decode the snapshot, return it.
|
// Decode the snapshot, return it.
|
||||||
let img = image::ImageReader::new(std::io::Cursor::new(snapshot_png_bytes))
|
let img = image::ImageReader::new(std::io::Cursor::new(snapshot_png_bytes))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Wasm bindings for `kcl`.
|
//! Wasm bindings for `kcl`.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::{str::FromStr, sync::Arc};
|
||||||
|
|
||||||
use futures::stream::TryStreamExt;
|
use futures::stream::TryStreamExt;
|
||||||
use gloo_utils::format::JsValueSerdeExt;
|
use gloo_utils::format::JsValueSerdeExt;
|
||||||
@ -56,10 +56,10 @@ pub async fn clear_scene_and_bust_cache(
|
|||||||
|
|
||||||
// wasm_bindgen wrapper for execute
|
// wasm_bindgen wrapper for execute
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn execute(
|
pub async fn execute_wasm(
|
||||||
program_ast_json: &str,
|
program_ast_json: &str,
|
||||||
program_memory_override_str: &str,
|
program_memory_override_str: &str,
|
||||||
settings: &str,
|
units: &str,
|
||||||
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
||||||
fs_manager: kcl_lib::wasm_engine::FileSystemManager,
|
fs_manager: kcl_lib::wasm_engine::FileSystemManager,
|
||||||
) -> Result<JsValue, String> {
|
) -> Result<JsValue, String> {
|
||||||
@ -73,11 +73,11 @@ pub async fn execute(
|
|||||||
// You cannot override the memory in non-mock mode.
|
// You cannot override the memory in non-mock mode.
|
||||||
let is_mock = program_memory_override.is_some();
|
let is_mock = program_memory_override.is_some();
|
||||||
|
|
||||||
let settings: kcl_lib::Configuration = serde_json::from_str(settings).map_err(|e| e.to_string())?;
|
let units = kcl_lib::UnitLength::from_str(units).map_err(|e| e.to_string())?;
|
||||||
let ctx = if is_mock {
|
let ctx = if is_mock {
|
||||||
kcl_lib::ExecutorContext::new_mock(fs_manager, settings.into()).await?
|
kcl_lib::ExecutorContext::new_mock(fs_manager, units).await?
|
||||||
} else {
|
} else {
|
||||||
kcl_lib::ExecutorContext::new(engine_manager, fs_manager, settings.into()).await?
|
kcl_lib::ExecutorContext::new(engine_manager, fs_manager, units).await?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut exec_state = ExecState::default();
|
let mut exec_state = ExecState::default();
|
||||||
@ -168,6 +168,23 @@ pub async fn make_default_planes(
|
|||||||
JsValue::from_serde(&default_planes).map_err(|e| e.to_string())
|
JsValue::from_serde(&default_planes).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wasm_bindgen wrapper for modifying the grid
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub async fn modify_grid(
|
||||||
|
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
||||||
|
hidden: bool,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
// deserialize the ast from a stringified json
|
||||||
|
|
||||||
|
let engine = kcl_lib::wasm_engine::EngineConnection::new(engine_manager)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
engine.modify_grid(hidden).await.map_err(String::from)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// wasm_bindgen wrapper for execute
|
// wasm_bindgen wrapper for execute
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn modify_ast_for_sketch_wasm(
|
pub async fn modify_ast_for_sketch_wasm(
|
||||||
@ -279,7 +296,7 @@ impl ServerConfig {
|
|||||||
pub async fn kcl_lsp_run(
|
pub async fn kcl_lsp_run(
|
||||||
config: ServerConfig,
|
config: ServerConfig,
|
||||||
engine_manager: Option<kcl_lib::wasm_engine::EngineCommandManager>,
|
engine_manager: Option<kcl_lib::wasm_engine::EngineCommandManager>,
|
||||||
settings: Option<String>,
|
units: &str,
|
||||||
token: String,
|
token: String,
|
||||||
baseurl: String,
|
baseurl: String,
|
||||||
) -> Result<(), JsValue> {
|
) -> Result<(), JsValue> {
|
||||||
@ -292,12 +309,8 @@ pub async fn kcl_lsp_run(
|
|||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
let executor_ctx = if let Some(engine_manager) = engine_manager {
|
let executor_ctx = if let Some(engine_manager) = engine_manager {
|
||||||
let settings: kcl_lib::Configuration = if let Some(settings) = settings {
|
let units = kcl_lib::UnitLength::from_str(units).map_err(|e| e.to_string())?;
|
||||||
serde_json::from_str(&settings).map_err(|e| e.to_string())?
|
Some(kcl_lib::ExecutorContext::new(engine_manager, fs.clone(), units).await?)
|
||||||
} else {
|
|
||||||
Default::default()
|
|
||||||
};
|
|
||||||
Some(kcl_lib::ExecutorContext::new(engine_manager, fs.clone(), settings.into()).await?)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -1,216 +0,0 @@
|
|||||||
//! Cache testing framework.
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use kcl_lib::ExecError;
|
|
||||||
|
|
||||||
struct Variation<'a> {
|
|
||||||
code: &'a str,
|
|
||||||
settings: &'a kcl_lib::ExecutorSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn cache_test(test_name: &str, variations: Vec<Variation<'_>>) -> Result<Vec<(String, image::DynamicImage)>> {
|
|
||||||
let first = variations
|
|
||||||
.first()
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("No variations provided for test '{}'", test_name))?;
|
|
||||||
|
|
||||||
let mut ctx = kcl_lib::ExecutorContext::new_with_client(first.settings.clone(), None, None).await?;
|
|
||||||
let mut exec_state = kcl_lib::ExecState::default();
|
|
||||||
|
|
||||||
let mut old_ast_state = None;
|
|
||||||
let mut img_results = Vec::new();
|
|
||||||
for (index, variation) in variations.iter().enumerate() {
|
|
||||||
let program = kcl_lib::Program::parse_no_errs(variation.code)?;
|
|
||||||
|
|
||||||
// set the new settings.
|
|
||||||
ctx.settings = variation.settings.clone();
|
|
||||||
|
|
||||||
ctx.run(
|
|
||||||
kcl_lib::CacheInformation {
|
|
||||||
old: old_ast_state,
|
|
||||||
new_ast: program.ast.clone(),
|
|
||||||
},
|
|
||||||
&mut exec_state,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let snapshot_png_bytes = ctx.prepare_snapshot().await?.contents.0;
|
|
||||||
|
|
||||||
// Decode the snapshot, return it.
|
|
||||||
let img = image::ImageReader::new(std::io::Cursor::new(snapshot_png_bytes))
|
|
||||||
.with_guessed_format()
|
|
||||||
.map_err(|e| ExecError::BadPng(e.to_string()))
|
|
||||||
.and_then(|x| x.decode().map_err(|e| ExecError::BadPng(e.to_string())))?;
|
|
||||||
// Save the snapshot.
|
|
||||||
let path = crate::assert_out(&format!("cache_{}_{}", test_name, index), &img);
|
|
||||||
|
|
||||||
img_results.push((path, img));
|
|
||||||
|
|
||||||
// Prepare the last state.
|
|
||||||
old_ast_state = Some(kcl_lib::OldAstState {
|
|
||||||
ast: program.ast,
|
|
||||||
exec_state: exec_state.clone(),
|
|
||||||
settings: variation.settings.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(img_results)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_cache_change_units_changes_output() {
|
|
||||||
let code = r#"part001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([5.5229, 5.25217], %)
|
|
||||||
|> line([10.50433, -1.19122], %)
|
|
||||||
|> line([8.01362, -5.48731], %)
|
|
||||||
|> line([-1.02877, -6.76825], %)
|
|
||||||
|> line([-11.53311, 2.81559], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(4, %)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = cache_test(
|
|
||||||
"change_units_changes_output",
|
|
||||||
vec![
|
|
||||||
Variation {
|
|
||||||
code,
|
|
||||||
settings: &kcl_lib::ExecutorSettings {
|
|
||||||
units: kcl_lib::UnitLength::In,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Variation {
|
|
||||||
code,
|
|
||||||
settings: &kcl_lib::ExecutorSettings {
|
|
||||||
units: kcl_lib::UnitLength::Mm,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let first = result.first().unwrap();
|
|
||||||
let second = result.last().unwrap();
|
|
||||||
|
|
||||||
assert!(first.1 != second.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_cache_change_grid_visualizes_grid_off_to_on() {
|
|
||||||
let code = r#"part001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([5.5229, 5.25217], %)
|
|
||||||
|> line([10.50433, -1.19122], %)
|
|
||||||
|> line([8.01362, -5.48731], %)
|
|
||||||
|> line([-1.02877, -6.76825], %)
|
|
||||||
|> line([-11.53311, 2.81559], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(4, %)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = cache_test(
|
|
||||||
"change_grid_visualizes_grid_off_to_on",
|
|
||||||
vec![
|
|
||||||
Variation {
|
|
||||||
code,
|
|
||||||
settings: &kcl_lib::ExecutorSettings {
|
|
||||||
show_grid: false,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Variation {
|
|
||||||
code,
|
|
||||||
settings: &kcl_lib::ExecutorSettings {
|
|
||||||
show_grid: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let first = result.first().unwrap();
|
|
||||||
let second = result.last().unwrap();
|
|
||||||
|
|
||||||
assert!(first.1 != second.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_cache_change_grid_visualizes_grid_on_to_off() {
|
|
||||||
let code = r#"part001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([5.5229, 5.25217], %)
|
|
||||||
|> line([10.50433, -1.19122], %)
|
|
||||||
|> line([8.01362, -5.48731], %)
|
|
||||||
|> line([-1.02877, -6.76825], %)
|
|
||||||
|> line([-11.53311, 2.81559], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(4, %)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = cache_test(
|
|
||||||
"change_grid_visualizes_grid_on_to_off",
|
|
||||||
vec![
|
|
||||||
Variation {
|
|
||||||
code,
|
|
||||||
settings: &kcl_lib::ExecutorSettings {
|
|
||||||
show_grid: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Variation {
|
|
||||||
code,
|
|
||||||
settings: &kcl_lib::ExecutorSettings {
|
|
||||||
show_grid: false,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let first = result.first().unwrap();
|
|
||||||
let second = result.last().unwrap();
|
|
||||||
|
|
||||||
assert!(first.1 != second.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_cache_change_highlight_edges_changes_visual() {
|
|
||||||
let code = r#"part001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([5.5229, 5.25217], %)
|
|
||||||
|> line([10.50433, -1.19122], %)
|
|
||||||
|> line([8.01362, -5.48731], %)
|
|
||||||
|> line([-1.02877, -6.76825], %)
|
|
||||||
|> line([-11.53311, 2.81559], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(4, %)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = cache_test(
|
|
||||||
"change_highlight_edges_changes_visual",
|
|
||||||
vec![
|
|
||||||
Variation {
|
|
||||||
code,
|
|
||||||
settings: &kcl_lib::ExecutorSettings {
|
|
||||||
highlight_edges: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Variation {
|
|
||||||
code,
|
|
||||||
settings: &kcl_lib::ExecutorSettings {
|
|
||||||
highlight_edges: false,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let first = result.first().unwrap();
|
|
||||||
let second = result.last().unwrap();
|
|
||||||
|
|
||||||
assert!(first.1 != second.1);
|
|
||||||
}
|
|
@ -1,5 +1,3 @@
|
|||||||
mod cache;
|
|
||||||
|
|
||||||
use kcl_lib::{
|
use kcl_lib::{
|
||||||
test_server::{execute_and_snapshot, execute_and_snapshot_no_auth},
|
test_server::{execute_and_snapshot, execute_and_snapshot_no_auth},
|
||||||
UnitLength,
|
UnitLength,
|
||||||
@ -7,7 +5,7 @@ use kcl_lib::{
|
|||||||
|
|
||||||
/// The minimum permissible difference between asserted twenty-twenty images.
|
/// The minimum permissible difference between asserted twenty-twenty images.
|
||||||
/// i.e. how different the current model snapshot can be from the previous saved one.
|
/// i.e. how different the current model snapshot can be from the previous saved one.
|
||||||
pub(crate) const MIN_DIFF: f64 = 0.99;
|
const MIN_DIFF: f64 = 0.99;
|
||||||
|
|
||||||
macro_rules! kcl_input {
|
macro_rules! kcl_input {
|
||||||
($file:literal) => {
|
($file:literal) => {
|
||||||
@ -15,11 +13,8 @@ macro_rules! kcl_input {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn assert_out(test_name: &str, result: &image::DynamicImage) -> String {
|
fn assert_out(test_name: &str, result: &image::DynamicImage) {
|
||||||
let path = format!("tests/executor/outputs/{test_name}.png");
|
twenty_twenty::assert_image(format!("tests/executor/outputs/{test_name}.png"), result, MIN_DIFF);
|
||||||
twenty_twenty::assert_image(&path, result, MIN_DIFF);
|
|
||||||
|
|
||||||
path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 47 KiB |