refactor code storage (#2144)
* refactor code storage Signed-off-by: Jess Frazelle <github@jessfraz.com> * typo Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * for now dont do onupdate its lagging the editor Signed-off-by: Jess Frazelle <github@jessfraz.com> * way smaller delay Signed-off-by: Jess Frazelle <github@jessfraz.com> * turn abck on on update Signed-off-by: Jess Frazelle <github@jessfraz.com> * dont be fancy Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix linter Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * empty * empty * good things Signed-off-by: Jess Frazelle <github@jessfraz.com> * empty * empty * 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> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * make less flakey Signed-off-by: Jess Frazelle <github@jessfraz.com> * go abck to errors for now Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { executeAst, executeCode } from 'useStore'
|
||||
import { executeAst } from 'useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { KCLError } from './errors'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
@ -16,17 +16,10 @@ import {
|
||||
SketchGroup,
|
||||
ExtrudeGroup,
|
||||
} from 'lang/wasm'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { getNodeFromPath } from './queryAst'
|
||||
import { Params } from 'react-router-dom'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
import { writeTextFile } from '@tauri-apps/plugin-fs'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
const PERSIST_CODE_TOKEN = 'persistCode'
|
||||
import { codeManager } from 'lib/singletons'
|
||||
|
||||
export class KclManager {
|
||||
private _code = bracket
|
||||
private _ast: Program = {
|
||||
body: [],
|
||||
start: 0,
|
||||
@ -44,7 +37,6 @@ export class KclManager {
|
||||
private _kclErrors: KCLError[] = []
|
||||
private _isExecuting = false
|
||||
private _wasmInitFailed = true
|
||||
private _params: Params<string> = {}
|
||||
|
||||
engineCommandManager: EngineCommandManager
|
||||
private _defferer = deferExecution((code: string) => {
|
||||
@ -62,7 +54,6 @@ export class KclManager {
|
||||
}, 600)
|
||||
|
||||
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
||||
private _codeCallBack: (arg: string) => void = () => {}
|
||||
private _astCallBack: (arg: Program) => void = () => {}
|
||||
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
||||
private _logsCallBack: (arg: string[]) => void = () => {}
|
||||
@ -78,28 +69,6 @@ export class KclManager {
|
||||
this._astCallBack(ast)
|
||||
}
|
||||
|
||||
get code() {
|
||||
return this._code
|
||||
}
|
||||
set code(code) {
|
||||
this._code = code
|
||||
this._codeCallBack(code)
|
||||
if (isTauri()) {
|
||||
setTimeout(() => {
|
||||
// Wait one event loop to give a chance for params to be set
|
||||
// Save the file to disk
|
||||
this._params.id &&
|
||||
writeTextFile(this._params.id, code).catch((err) => {
|
||||
// TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
||||
console.error('error saving file', err)
|
||||
toast.error('Error saving file, please check file permissions')
|
||||
})
|
||||
})
|
||||
} else {
|
||||
safteLSSetItem(PERSIST_CODE_TOKEN, code)
|
||||
}
|
||||
}
|
||||
|
||||
get programMemory() {
|
||||
return this._programMemory
|
||||
}
|
||||
@ -140,38 +109,15 @@ export class KclManager {
|
||||
this._wasmInitFailedCallback(wasmInitFailed)
|
||||
}
|
||||
|
||||
setParams(params: Params<string>) {
|
||||
this._params = params
|
||||
}
|
||||
|
||||
constructor(engineCommandManager: EngineCommandManager) {
|
||||
this.engineCommandManager = engineCommandManager
|
||||
|
||||
if (isTauri()) {
|
||||
this.code = ''
|
||||
return
|
||||
}
|
||||
const storedCode = safeLSGetItem(PERSIST_CODE_TOKEN) || ''
|
||||
// TODO #819 remove zustand persistence logic in a few months
|
||||
// short term migration, shouldn't make a difference for tauri app users
|
||||
// anyway since that's filesystem based.
|
||||
const zustandStore = JSON.parse(safeLSGetItem('store') || '{}')
|
||||
if (storedCode === null && zustandStore?.state?.code) {
|
||||
this.code = zustandStore.state.code
|
||||
safteLSSetItem(PERSIST_CODE_TOKEN, this._code)
|
||||
zustandStore.state.code = ''
|
||||
safteLSSetItem('store', JSON.stringify(zustandStore))
|
||||
} else if (storedCode === null) {
|
||||
this.code = bracket
|
||||
} else {
|
||||
this.code = storedCode
|
||||
}
|
||||
this.ensureWasmInit().then(() => {
|
||||
this.ast = this.safeParse(this.code) || this.ast
|
||||
this.ast = this.safeParse(codeManager.code) || this.ast
|
||||
})
|
||||
}
|
||||
|
||||
registerCallBacks({
|
||||
setCode,
|
||||
setProgramMemory,
|
||||
setAst,
|
||||
setLogs,
|
||||
@ -179,7 +125,6 @@ export class KclManager {
|
||||
setIsExecuting,
|
||||
setWasmInitFailed,
|
||||
}: {
|
||||
setCode: (arg: string) => void
|
||||
setProgramMemory: (arg: ProgramMemory) => void
|
||||
setAst: (arg: Program) => void
|
||||
setLogs: (arg: string[]) => void
|
||||
@ -187,7 +132,6 @@ export class KclManager {
|
||||
setIsExecuting: (arg: boolean) => void
|
||||
setWasmInitFailed: (arg: boolean) => void
|
||||
}) {
|
||||
this._codeCallBack = setCode
|
||||
this._programMemoryCallBack = setProgramMemory
|
||||
this._astCallBack = setAst
|
||||
this._logsCallBack = setLogs
|
||||
@ -227,12 +171,11 @@ export class KclManager {
|
||||
|
||||
private _cancelTokens: Map<number, boolean> = new Map()
|
||||
|
||||
async executeAst(
|
||||
ast: Program = this._ast,
|
||||
updateCode = false,
|
||||
executionId?: number
|
||||
) {
|
||||
if (!this.engineCommandManager.engineConnection?.isReady()) return
|
||||
// This NEVER updates the code, if you want to update the code DO NOT add to
|
||||
// this function, too many other things that don't want it exist.
|
||||
// just call to codeManager from wherever you want in other files.
|
||||
async executeAst(ast: Program = this._ast, executionId?: number) {
|
||||
await this?.engineCommandManager?.waitForReady
|
||||
const currentExecutionId = executionId || Date.now()
|
||||
this._cancelTokens.set(currentExecutionId, false)
|
||||
|
||||
@ -253,9 +196,6 @@ export class KclManager {
|
||||
this.kclErrors = errors
|
||||
this.programMemory = programMemory
|
||||
this.ast = { ...ast }
|
||||
if (updateCode) {
|
||||
this.code = recast(ast)
|
||||
}
|
||||
this._executeCallback()
|
||||
this.engineCommandManager.addCommandLog({
|
||||
type: 'execution-done',
|
||||
@ -263,22 +203,24 @@ export class KclManager {
|
||||
})
|
||||
this._cancelTokens.delete(currentExecutionId)
|
||||
}
|
||||
// NOTE: this always updates the code state and editor.
|
||||
// DO NOT CALL THIS from codemirror ever.
|
||||
async executeAstMock(
|
||||
ast: Program = this._ast,
|
||||
{
|
||||
updates,
|
||||
}: {
|
||||
updates: 'none' | 'code' | 'codeAndArtifactRanges'
|
||||
updates: 'none' | 'artifactRanges'
|
||||
} = { updates: 'none' }
|
||||
) {
|
||||
await this.ensureWasmInit()
|
||||
const newCode = recast(ast)
|
||||
const newAst = this.safeParse(newCode)
|
||||
if (!newAst) return
|
||||
codeManager.updateCodeStateEditor(newCode)
|
||||
// Write the file to disk.
|
||||
await codeManager.writeToFile()
|
||||
await this?.engineCommandManager?.waitForReady
|
||||
if (updates !== 'none') {
|
||||
this.setCode(recast(ast))
|
||||
}
|
||||
this._ast = { ...newAst }
|
||||
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
@ -289,7 +231,7 @@ export class KclManager {
|
||||
this._logs = logs
|
||||
this._kclErrors = errors
|
||||
this._programMemory = programMemory
|
||||
if (updates !== 'codeAndArtifactRanges') return
|
||||
if (updates !== 'artifactRanges') return
|
||||
Object.entries(this.engineCommandManager.artifactMap).forEach(
|
||||
([commandId, artifact]) => {
|
||||
if (!artifact.pathToNode) return
|
||||
@ -309,79 +251,32 @@ export class KclManager {
|
||||
}
|
||||
)
|
||||
}
|
||||
executeCode = async (code?: string, executionId?: number) => {
|
||||
const currentExecutionId = executionId || Date.now()
|
||||
this._cancelTokens.set(currentExecutionId, false)
|
||||
if (this._cancelTokens.get(currentExecutionId)) {
|
||||
this._cancelTokens.delete(currentExecutionId)
|
||||
return
|
||||
}
|
||||
await this.ensureWasmInit()
|
||||
await this?.engineCommandManager?.waitForReady
|
||||
const result = await executeCode({
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
code: code || this._code,
|
||||
lastAst: this._ast,
|
||||
force: false,
|
||||
})
|
||||
// Check the cancellation token for this execution before applying side effects
|
||||
if (this._cancelTokens.get(currentExecutionId)) {
|
||||
this._cancelTokens.delete(currentExecutionId)
|
||||
return
|
||||
}
|
||||
if (!result.isChange) return
|
||||
const { logs, errors, programMemory, ast } = result
|
||||
enterEditMode(programMemory, this.engineCommandManager)
|
||||
this.logs = logs
|
||||
this.kclErrors = errors
|
||||
this.programMemory = programMemory
|
||||
this.ast = ast
|
||||
if (code) this.code = code
|
||||
this._cancelTokens.delete(currentExecutionId)
|
||||
}
|
||||
cancelAllExecutions() {
|
||||
this._cancelTokens.forEach((_, key) => {
|
||||
this._cancelTokens.set(key, true)
|
||||
})
|
||||
}
|
||||
setCode(code: string, shouldWriteFile = true) {
|
||||
if (shouldWriteFile) {
|
||||
// use the normal code setter
|
||||
this.code = code
|
||||
return
|
||||
}
|
||||
this._code = code
|
||||
this._codeCallBack(code)
|
||||
}
|
||||
setCodeAndExecute(code: string, shouldWriteFile = true) {
|
||||
this.setCode(code, shouldWriteFile)
|
||||
if (code.trim()) {
|
||||
this._defferer(code)
|
||||
return
|
||||
}
|
||||
this._ast = {
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
nonCodeMeta: {
|
||||
nonCodeNodes: {},
|
||||
start: [],
|
||||
},
|
||||
}
|
||||
this._programMemory = {
|
||||
root: {},
|
||||
return: null,
|
||||
}
|
||||
this.engineCommandManager.endSession()
|
||||
executeCode(force?: boolean) {
|
||||
// If we want to force it we don't want to defer it.
|
||||
if (!force) return this._defferer(codeManager.code)
|
||||
return this.executeAst()
|
||||
}
|
||||
format() {
|
||||
const ast = this.safeParse(this.code)
|
||||
const originalCode = codeManager.code
|
||||
const ast = this.safeParse(originalCode)
|
||||
if (!ast) return
|
||||
this.code = recast(ast)
|
||||
const code = recast(ast)
|
||||
if (originalCode === code) return
|
||||
|
||||
// Update the code state and the editor.
|
||||
codeManager.updateCodeStateEditor(code)
|
||||
// Write back to the file system.
|
||||
codeManager.writeToFile()
|
||||
}
|
||||
// There's overlapping responsibility between updateAst and executeAst.
|
||||
// updateAst was added as it was used a lot before xState migration so makes the port easier.
|
||||
// but should probably have think about which of the function to keep
|
||||
// This always updates the code state and editor and writes to the file system.
|
||||
async updateAst(
|
||||
ast: Program,
|
||||
execute: boolean,
|
||||
@ -414,12 +309,17 @@ export class KclManager {
|
||||
|
||||
if (execute) {
|
||||
// Call execute on the set ast.
|
||||
await this.executeAst(astWithUpdatedSource, true)
|
||||
// Update the code state and editor.
|
||||
codeManager.updateCodeStateEditor(newCode)
|
||||
// Write the file to disk.
|
||||
await codeManager.writeToFile()
|
||||
await this.executeAst(astWithUpdatedSource)
|
||||
} else {
|
||||
// When we don't re-execute, we still want to update the program
|
||||
// memory with the new ast. So we will hit the mock executor
|
||||
// instead.
|
||||
await this.executeAstMock(astWithUpdatedSource, { updates: 'code' })
|
||||
// instead..
|
||||
// Execute ast mock will update the code state and editor.
|
||||
await this.executeAstMock(astWithUpdatedSource)
|
||||
}
|
||||
return returnVal
|
||||
}
|
||||
@ -443,16 +343,6 @@ export class KclManager {
|
||||
}
|
||||
}
|
||||
|
||||
function safeLSGetItem(key: string) {
|
||||
if (typeof window === 'undefined') return null
|
||||
return localStorage?.getItem(key)
|
||||
}
|
||||
|
||||
function safteLSSetItem(key: string, value: string) {
|
||||
if (typeof window === 'undefined') return
|
||||
localStorage?.setItem(key, value)
|
||||
}
|
||||
|
||||
function enterEditMode(
|
||||
programMemory: ProgramMemory,
|
||||
engineCommandManager: EngineCommandManager
|
||||
|
Reference in New Issue
Block a user