remove clearScene from TS side (#4684)
* updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix lint Signed-off-by: Jess Frazelle <github@jessfraz.com> * add failing tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * more tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * the scene is cleared Signed-off-by: Jess Frazelle <github@jessfraz.com> * create new clear scene and bust cache function from rust side Signed-off-by: Jess Frazelle <github@jessfraz.com> * pull thru Signed-off-by: Jess Frazelle <github@jessfraz.com> * set that we switched files Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix two dirties Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
@ -136,6 +136,335 @@ test(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
const errorDir = join(dir, 'broken-code')
|
||||||
|
await fsp.mkdir(errorDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('broken-code-test.kcl'),
|
||||||
|
join(errorDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
||||||
|
await page.getByTestId('app-logo').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
||||||
|
await expect(page.getByText('broken-code')).toBeVisible()
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
await expect(page.getByText('New Project')).toBeVisible()
|
||||||
|
})
|
||||||
|
await test.step('opening broken code project should clear the scene and show the error', async () => {
|
||||||
|
// Go back home.
|
||||||
|
await expect(page.getByText('broken-code')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('broken-code').click()
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
const crypticErrorText = `Expected a tag declarator`
|
||||||
|
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||||
|
|
||||||
|
// black pixel means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
const emptyDir = join(dir, 'empty')
|
||||||
|
await fsp.mkdir(emptyDir, { recursive: true })
|
||||||
|
await fsp.writeFile(join(emptyDir, 'main.kcl'), '')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
||||||
|
await page.getByTestId('app-logo').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
||||||
|
await expect(page.getByText('empty')).toBeVisible()
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
await expect(page.getByText('New Project')).toBeVisible()
|
||||||
|
})
|
||||||
|
await test.step('opening empty code project should clear the scene', async () => {
|
||||||
|
// Go back home.
|
||||||
|
await expect(page.getByText('empty')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('empty').click()
|
||||||
|
|
||||||
|
// Ensure the code is empty.
|
||||||
|
await expect(u.codeLocator).toContainText('')
|
||||||
|
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
|
||||||
|
|
||||||
|
// planes colors means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open empty file, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
|
||||||
|
await fsp.writeFile(join(bracketDir, 'empty.kcl'), '')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
await test.step('creating a empty file should clear the scene', async () => {
|
||||||
|
// open the file pane.
|
||||||
|
await page.getByTestId('files-pane-button').click()
|
||||||
|
|
||||||
|
// OPen the other file.
|
||||||
|
const file = page.getByRole('button', { name: 'empty.kcl' })
|
||||||
|
await expect(file).toBeVisible()
|
||||||
|
|
||||||
|
await file.click()
|
||||||
|
|
||||||
|
// planes colors means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
|
||||||
|
// Ensure the code is empty.
|
||||||
|
await expect(u.codeLocator).toContainText('')
|
||||||
|
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('broken-code-test.kcl'),
|
||||||
|
join(bracketDir, 'broken-code-test.kcl')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
await test.step('opening broken code file should clear the scene and show the error', async () => {
|
||||||
|
// open the file pane.
|
||||||
|
await page.getByTestId('files-pane-button').click()
|
||||||
|
|
||||||
|
// OPen the other file.
|
||||||
|
const file = page.getByRole('button', { name: 'broken-code-test.kcl' })
|
||||||
|
await expect(file).toBeVisible()
|
||||||
|
|
||||||
|
await file.click()
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
const crypticErrorText = `Expected a tag declarator`
|
||||||
|
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||||
|
|
||||||
|
// black pixel means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'when code with error first loads you get errors in console',
|
'when code with error first loads you get errors in console',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
|
@ -266,6 +266,7 @@ const FileTreeItem = ({
|
|||||||
// Let the lsp servers know we closed a file.
|
// Let the lsp servers know we closed a file.
|
||||||
onFileClose(currentFile?.path || null, project?.path || null)
|
onFileClose(currentFile?.path || null, project?.path || null)
|
||||||
onFileOpen(fileOrDir.path, project?.path || null)
|
onFileOpen(fileOrDir.path, project?.path || null)
|
||||||
|
kclManager.switchedFiles = true
|
||||||
|
|
||||||
// Open kcl files
|
// Open kcl files
|
||||||
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
||||||
|
@ -40,7 +40,9 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
|
|||||||
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
|
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<button
|
<button
|
||||||
onClick={() => kclManager.format()}
|
onClick={() => {
|
||||||
|
kclManager.format().catch(reportRejection)
|
||||||
|
}}
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
>
|
>
|
||||||
<span>Format code</span>
|
<span>Format code</span>
|
||||||
|
@ -10,7 +10,7 @@ import { APP_NAME } from 'lib/constants'
|
|||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
import { useLspContext } from './LspProvider'
|
import { useLspContext } from './LspProvider'
|
||||||
import { engineCommandManager } from 'lib/singletons'
|
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
@ -68,8 +68,7 @@ function AppLogoLink({
|
|||||||
data-testid="app-logo"
|
data-testid="app-logo"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onProjectClose(file || null, project?.path || null, false)
|
onProjectClose(file || null, project?.path || null, false)
|
||||||
// Clear the scene.
|
kclManager.switchedFiles = true
|
||||||
engineCommandManager.clearScene()
|
|
||||||
}}
|
}}
|
||||||
to={PATHS.HOME}
|
to={PATHS.HOME}
|
||||||
className={wrapperClassName + ' hover:before:brightness-110'}
|
className={wrapperClassName + ' hover:before:brightness-110'}
|
||||||
@ -190,8 +189,7 @@ function ProjectMenuPopover({
|
|||||||
className: !isDesktop() ? 'hidden' : '',
|
className: !isDesktop() ? 'hidden' : '',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
onProjectClose(file || null, project?.path || null, true)
|
onProjectClose(file || null, project?.path || null, true)
|
||||||
// Clear the scene.
|
kclManager.switchedFiles = true
|
||||||
engineCommandManager.clearScene()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
].filter(
|
].filter(
|
||||||
|
@ -12,6 +12,7 @@ import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
CallExpression,
|
CallExpression,
|
||||||
|
clearSceneAndBustCache,
|
||||||
emptyExecState,
|
emptyExecState,
|
||||||
ExecState,
|
ExecState,
|
||||||
initPromise,
|
initPromise,
|
||||||
@ -60,6 +61,7 @@ export class KclManager {
|
|||||||
private _executeIsStale: ExecuteArgs | null = null
|
private _executeIsStale: ExecuteArgs | null = null
|
||||||
private _wasmInitFailed = true
|
private _wasmInitFailed = true
|
||||||
private _hasErrors = false
|
private _hasErrors = false
|
||||||
|
private _switchedFiles = false
|
||||||
|
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
|
|
||||||
@ -79,6 +81,10 @@ export class KclManager {
|
|||||||
this._astCallBack(ast)
|
this._astCallBack(ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set switchedFiles(switchedFiles: boolean) {
|
||||||
|
this._switchedFiles = switchedFiles
|
||||||
|
}
|
||||||
|
|
||||||
get programMemory() {
|
get programMemory() {
|
||||||
return this._programMemory
|
return this._programMemory
|
||||||
}
|
}
|
||||||
@ -166,8 +172,12 @@ export class KclManager {
|
|||||||
this.engineCommandManager = engineCommandManager
|
this.engineCommandManager = engineCommandManager
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.ensureWasmInit().then(() => {
|
this.ensureWasmInit().then(async () => {
|
||||||
this.ast = this.safeParse(codeManager.code) || this.ast
|
await this.safeParse(codeManager.code).then((ast) => {
|
||||||
|
if (ast) {
|
||||||
|
this.ast = ast
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +221,25 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
safeParse(code: string): Node<Program> | null {
|
// (jess) I'm not in love with this, but it ensures we clear the scene and
|
||||||
|
// bust the cache on
|
||||||
|
// errors from parsing when opening new files.
|
||||||
|
// Why not just clear the cache on all parse errors, you ask? well its actually
|
||||||
|
// really nice to keep the cache on parse errors within the same file, and
|
||||||
|
// only bust on engine errors esp if they take a long time to execute and
|
||||||
|
// you hit the wrong key!
|
||||||
|
private async checkIfSwitchedFilesShouldClear() {
|
||||||
|
// If we were switching files and we hit an error on parse we need to bust
|
||||||
|
// the cache and clear the scene.
|
||||||
|
if (this._hasErrors && this._switchedFiles) {
|
||||||
|
await clearSceneAndBustCache(this.engineCommandManager)
|
||||||
|
} else if (this._switchedFiles) {
|
||||||
|
// Reset the switched files boolean.
|
||||||
|
this._switchedFiles = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async safeParse(code: string): Promise<Node<Program> | null> {
|
||||||
const result = parse(code)
|
const result = parse(code)
|
||||||
this.diagnostics = []
|
this.diagnostics = []
|
||||||
this._hasErrors = false
|
this._hasErrors = false
|
||||||
@ -220,6 +248,8 @@ export class KclManager {
|
|||||||
const kclerror: KCLError = result as KCLError
|
const kclerror: KCLError = result as KCLError
|
||||||
this.diagnostics = kclErrorsToDiagnostics([kclerror])
|
this.diagnostics = kclErrorsToDiagnostics([kclerror])
|
||||||
this._hasErrors = true
|
this._hasErrors = true
|
||||||
|
|
||||||
|
await this.checkIfSwitchedFilesShouldClear()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +258,7 @@ export class KclManager {
|
|||||||
if (result.errors.length > 0) {
|
if (result.errors.length > 0) {
|
||||||
this._hasErrors = true
|
this._hasErrors = true
|
||||||
|
|
||||||
|
await this.checkIfSwitchedFilesShouldClear()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +384,7 @@ export class KclManager {
|
|||||||
console.error(newCode)
|
console.error(newCode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newAst = this.safeParse(newCode)
|
const newAst = await this.safeParse(newCode)
|
||||||
if (!newAst) {
|
if (!newAst) {
|
||||||
this.clearAst()
|
this.clearAst()
|
||||||
return
|
return
|
||||||
@ -408,7 +439,7 @@ export class KclManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
async executeCode(zoomToFit?: boolean): Promise<void> {
|
async executeCode(zoomToFit?: boolean): Promise<void> {
|
||||||
const ast = this.safeParse(codeManager.code)
|
const ast = await this.safeParse(codeManager.code)
|
||||||
if (!ast) {
|
if (!ast) {
|
||||||
this.clearAst()
|
this.clearAst()
|
||||||
return
|
return
|
||||||
@ -416,9 +447,9 @@ export class KclManager {
|
|||||||
this.ast = { ...ast }
|
this.ast = { ...ast }
|
||||||
return this.executeAst({ zoomToFit })
|
return this.executeAst({ zoomToFit })
|
||||||
}
|
}
|
||||||
format() {
|
async format() {
|
||||||
const originalCode = codeManager.code
|
const originalCode = codeManager.code
|
||||||
const ast = this.safeParse(originalCode)
|
const ast = await this.safeParse(originalCode)
|
||||||
if (!ast) {
|
if (!ast) {
|
||||||
this.clearAst()
|
this.clearAst()
|
||||||
return
|
return
|
||||||
@ -458,7 +489,7 @@ export class KclManager {
|
|||||||
const newCode = recast(ast)
|
const newCode = recast(ast)
|
||||||
if (err(newCode)) return Promise.reject(newCode)
|
if (err(newCode)) return Promise.reject(newCode)
|
||||||
|
|
||||||
const astWithUpdatedSource = this.safeParse(newCode)
|
const astWithUpdatedSource = await this.safeParse(newCode)
|
||||||
if (!astWithUpdatedSource) return Promise.reject(new Error('bad ast'))
|
if (!astWithUpdatedSource) return Promise.reject(new Error('bad ast'))
|
||||||
let returnVal: Selections | undefined = undefined
|
let returnVal: Selections | undefined = undefined
|
||||||
|
|
||||||
|
@ -1879,17 +1879,6 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
return JSON.stringify(this.defaultPlanes)
|
return JSON.stringify(this.defaultPlanes)
|
||||||
}
|
}
|
||||||
clearScene(): void {
|
|
||||||
const deleteCmd: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'scene_clear_all',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
this.clearDefaultPlanes()
|
|
||||||
this.engineConnection?.send(deleteCmd)
|
|
||||||
}
|
|
||||||
addCommandLog(message: CommandLog) {
|
addCommandLog(message: CommandLog) {
|
||||||
if (this.commandLogs.length > 500) {
|
if (this.commandLogs.length > 500) {
|
||||||
this.commandLogs.shift()
|
this.commandLogs.shift()
|
||||||
|
@ -16,6 +16,7 @@ import init, {
|
|||||||
parse_project_settings,
|
parse_project_settings,
|
||||||
default_project_settings,
|
default_project_settings,
|
||||||
base64_decode,
|
base64_decode,
|
||||||
|
clear_scene_and_bust_cache,
|
||||||
} from '../wasm-lib/pkg/wasm_lib'
|
} from '../wasm-lib/pkg/wasm_lib'
|
||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||||
@ -698,6 +699,21 @@ export function defaultAppSettings(): DeepPartial<Configuration> | Error {
|
|||||||
return default_app_settings()
|
return default_app_settings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function clearSceneAndBustCache(
|
||||||
|
engineCommandManager: EngineCommandManager
|
||||||
|
): Promise<null | Error> {
|
||||||
|
try {
|
||||||
|
await clear_scene_and_bust_cache(engineCommandManager)
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('clear_scene_and_bust_cache: error', e)
|
||||||
|
return Promise.reject(
|
||||||
|
new Error(`Error on clear_scene_and_bust_cache: ${e}`)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export function parseAppSettings(
|
export function parseAppSettings(
|
||||||
toml: string
|
toml: string
|
||||||
): DeepPartial<Configuration> | Error {
|
): DeepPartial<Configuration> | Error {
|
||||||
|
@ -13,7 +13,6 @@ import {
|
|||||||
listProjects,
|
listProjects,
|
||||||
readAppSettingsFile,
|
readAppSettingsFile,
|
||||||
} from './desktop'
|
} from './desktop'
|
||||||
import { engineCommandManager } from './singletons'
|
|
||||||
|
|
||||||
export const isHidden = (fileOrDir: FileEntry) =>
|
export const isHidden = (fileOrDir: FileEntry) =>
|
||||||
!!fileOrDir.name?.startsWith('.')
|
!!fileOrDir.name?.startsWith('.')
|
||||||
@ -116,9 +115,6 @@ export async function createAndOpenNewTutorialProject({
|
|||||||
) => void
|
) => void
|
||||||
navigate: (path: string) => void
|
navigate: (path: string) => void
|
||||||
}) {
|
}) {
|
||||||
// Clear the scene.
|
|
||||||
engineCommandManager.clearScene()
|
|
||||||
|
|
||||||
// Create a new project with the onboarding project name
|
// Create a new project with the onboarding project name
|
||||||
const configuration = await readAppSettingsFile()
|
const configuration = await readAppSettingsFile()
|
||||||
const projects = await listProjects(configuration)
|
const projects = await listProjects(configuration)
|
||||||
|
@ -5,7 +5,7 @@ import { isDesktop } from './isDesktop'
|
|||||||
import { FILE_EXT, PROJECT_SETTINGS_FILE_NAME } from './constants'
|
import { FILE_EXT, PROJECT_SETTINGS_FILE_NAME } from './constants'
|
||||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
import { parseProjectSettings } from 'lang/wasm'
|
import { parseProjectSettings } from 'lang/wasm'
|
||||||
import { err } from './trap'
|
import { err, reportRejection } from './trap'
|
||||||
import { projectConfigurationToSettingsPayload } from './settings/settingsUtils'
|
import { projectConfigurationToSettingsPayload } from './settings/settingsUtils'
|
||||||
|
|
||||||
interface OnSubmitProps {
|
interface OnSubmitProps {
|
||||||
@ -28,7 +28,7 @@ export function kclCommands(
|
|||||||
groupId: 'code',
|
groupId: 'code',
|
||||||
icon: 'code',
|
icon: 'code',
|
||||||
onSubmit: () => {
|
onSubmit: () => {
|
||||||
kclManager.format()
|
kclManager.format().catch(reportRejection)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -3,8 +3,10 @@ pub(crate) mod digest;
|
|||||||
pub mod modify;
|
pub mod modify;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
use crate::parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject};
|
use crate::{
|
||||||
use crate::source_range::ModuleId;
|
parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject},
|
||||||
|
source_range::ModuleId,
|
||||||
|
};
|
||||||
|
|
||||||
impl BodyItem {
|
impl BodyItem {
|
||||||
pub fn module_id(&self) -> ModuleId {
|
pub fn module_id(&self) -> ModuleId {
|
||||||
|
@ -8,7 +8,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use parse_display::{Display, FromStr};
|
use parse_display::{Display, FromStr};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -4,7 +4,9 @@ use std::{str::FromStr, sync::Arc};
|
|||||||
|
|
||||||
use futures::stream::TryStreamExt;
|
use futures::stream::TryStreamExt;
|
||||||
use gloo_utils::format::JsValueSerdeExt;
|
use gloo_utils::format::JsValueSerdeExt;
|
||||||
use kcl_lib::{CacheInformation, CoreDump, EngineManager, ExecState, ModuleId, OldAstState, Program};
|
use kcl_lib::{
|
||||||
|
exec::IdGenerator, CacheInformation, CoreDump, EngineManager, ExecState, ModuleId, OldAstState, Program,
|
||||||
|
};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tower_lsp::{LspService, Server};
|
use tower_lsp::{LspService, Server};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
@ -22,6 +24,36 @@ async fn read_old_ast_memory() -> Option<OldAstState> {
|
|||||||
lock.clone()
|
lock.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn bust_cache() {
|
||||||
|
// We don't use the cache in mock mode.
|
||||||
|
let mut current_cache = OLD_AST_MEMORY.write().await;
|
||||||
|
// Set the cache to None.
|
||||||
|
*current_cache = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wasm_bindgen wrapper for clearing the scene and busting the cache.
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub async fn clear_scene_and_bust_cache(
|
||||||
|
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
// Bust the cache.
|
||||||
|
bust_cache().await;
|
||||||
|
|
||||||
|
let engine = kcl_lib::wasm_engine::EngineConnection::new(engine_manager)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
|
||||||
|
let mut id_generator: IdGenerator = Default::default();
|
||||||
|
engine
|
||||||
|
.clear_scene(&mut id_generator, Default::default())
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// wasm_bindgen wrapper for execute
|
// wasm_bindgen wrapper for execute
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn execute_wasm(
|
pub async fn execute_wasm(
|
||||||
@ -74,10 +106,7 @@ pub async fn execute_wasm(
|
|||||||
.map_err(String::from)
|
.map_err(String::from)
|
||||||
{
|
{
|
||||||
if !is_mock {
|
if !is_mock {
|
||||||
// We don't use the cache in mock mode.
|
bust_cache().await;
|
||||||
let mut current_cache = OLD_AST_MEMORY.write().await;
|
|
||||||
// Set the cache to None.
|
|
||||||
*current_cache = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw the error.
|
// Throw the error.
|
||||||
|
Reference in New Issue
Block a user