2025-04-01 23:54:26 -07:00
|
|
|
import type { Diagnostic } from '@codemirror/lint'
|
|
|
|
import type {
|
|
|
|
EntityType_type,
|
|
|
|
ModelingCmdReq_type,
|
|
|
|
} from '@kittycad/lib/dist/types/src/models'
|
2025-04-07 07:08:31 -04:00
|
|
|
import type { SceneInfra } from '@src/clientSideScene/sceneInfra'
|
|
|
|
import type EditorManager from '@src/editor/manager'
|
|
|
|
import type CodeManager from '@src/lang/codeManager'
|
|
|
|
import type RustContext from '@src/lib/rustContext'
|
2025-04-01 23:54:26 -07:00
|
|
|
|
2025-04-03 07:31:18 +11:00
|
|
|
import type { KclValue } from '@rust/kcl-lib/bindings/KclValue'
|
2025-04-01 23:54:26 -07:00
|
|
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
|
|
|
import type { Operation } from '@rust/kcl-lib/bindings/Operation'
|
|
|
|
|
|
|
|
import type { KCLError } from '@src/lang/errors'
|
2025-04-01 15:31:19 -07:00
|
|
|
import {
|
|
|
|
compilationErrorsToDiagnostics,
|
|
|
|
kclErrorsToDiagnostics,
|
2025-04-01 23:54:26 -07:00
|
|
|
} from '@src/lang/errors'
|
|
|
|
import { executeAst, executeAstMock, lintAst } from '@src/lang/langHelpers'
|
|
|
|
import { getNodeFromPath, getSettingsAnnotation } from '@src/lang/queryAst'
|
2025-04-14 12:09:45 -04:00
|
|
|
import { CommandLogType } from '@src/lang/std/commandLog'
|
2025-04-01 23:54:26 -07:00
|
|
|
import type { EngineCommandManager } from '@src/lang/std/engineConnection'
|
|
|
|
import { topLevelRange } from '@src/lang/util'
|
|
|
|
import type {
|
|
|
|
ArtifactGraph,
|
2025-04-01 15:31:19 -07:00
|
|
|
ExecState,
|
|
|
|
PathToNode,
|
|
|
|
Program,
|
|
|
|
VariableMap,
|
2025-04-01 23:54:26 -07:00
|
|
|
} from '@src/lang/wasm'
|
2025-04-11 11:17:46 -07:00
|
|
|
import { emptyExecState, getKclVersion, parse, recast } from '@src/lang/wasm'
|
|
|
|
import { initPromise } from '@src/lang/wasmUtils'
|
2025-04-01 23:54:26 -07:00
|
|
|
import type { ArtifactIndex } from '@src/lib/artifactIndex'
|
|
|
|
import { buildArtifactIndex } from '@src/lib/artifactIndex'
|
2025-04-03 07:31:18 +11:00
|
|
|
import {
|
|
|
|
DEFAULT_DEFAULT_LENGTH_UNIT,
|
|
|
|
EXECUTE_AST_INTERRUPT_ERROR_MESSAGE,
|
|
|
|
} from '@src/lib/constants'
|
2025-04-01 23:54:26 -07:00
|
|
|
import { markOnce } from '@src/lib/performance'
|
|
|
|
import type { Selections } from '@src/lib/selections'
|
|
|
|
import { handleSelectionBatch } from '@src/lib/selections'
|
2025-04-03 07:31:18 +11:00
|
|
|
import type {
|
|
|
|
BaseUnit,
|
|
|
|
KclSettingsAnnotation,
|
|
|
|
} from '@src/lib/settings/settingsTypes'
|
2025-04-01 23:54:26 -07:00
|
|
|
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
2025-04-07 07:08:31 -04:00
|
|
|
|
2025-04-01 23:54:26 -07:00
|
|
|
import { err, reportRejection } from '@src/lib/trap'
|
2025-04-15 23:27:50 +10:00
|
|
|
import { deferExecution, uuidv4 } from '@src/lib/utils'
|
2025-04-25 18:21:19 +02:00
|
|
|
import type { PlaneVisibilityMap } from '@src/machines/modelingMachine'
|
2023-10-11 13:36:54 +11:00
|
|
|
|
2024-08-14 08:49:00 -07:00
|
|
|
interface ExecuteArgs {
|
2024-10-30 16:52:17 -04:00
|
|
|
ast?: Node<Program>
|
2024-08-14 08:49:00 -07:00
|
|
|
executionId?: number
|
|
|
|
}
|
|
|
|
|
2025-04-07 07:08:31 -04:00
|
|
|
// Each of our singletons has dependencies on _other_ singletons, so importing
|
|
|
|
// can easily become cyclic. Each will have its own Singletons type.
|
|
|
|
interface Singletons {
|
|
|
|
rustContext: RustContext
|
|
|
|
codeManager: CodeManager
|
|
|
|
editorManager: EditorManager
|
|
|
|
sceneInfra: SceneInfra
|
|
|
|
}
|
|
|
|
|
2024-03-22 16:55:30 +11:00
|
|
|
export class KclManager {
|
2025-03-29 17:25:26 -07:00
|
|
|
/**
|
|
|
|
* The artifactGraph is a client-side representation of the commands that have been sent
|
|
|
|
* see: src/lang/std/artifactGraph-README.md for a full explanation.
|
|
|
|
*/
|
|
|
|
artifactGraph: ArtifactGraph = new Map()
|
|
|
|
artifactIndex: ArtifactIndex = []
|
|
|
|
|
2024-10-30 16:52:17 -04:00
|
|
|
private _ast: Node<Program> = {
|
2023-10-11 13:36:54 +11:00
|
|
|
body: [],
|
2024-11-26 16:39:57 +13:00
|
|
|
shebang: null,
|
2023-10-11 13:36:54 +11:00
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-11-07 11:23:41 -05:00
|
|
|
moduleId: 0,
|
2023-10-11 13:36:54 +11:00
|
|
|
nonCodeMeta: {
|
|
|
|
nonCodeNodes: {},
|
2024-10-30 16:52:17 -04:00
|
|
|
startNodes: [],
|
2023-10-11 13:36:54 +11:00
|
|
|
},
|
2025-02-13 16:17:09 +13:00
|
|
|
innerAttrs: [],
|
|
|
|
outerAttrs: [],
|
2025-03-20 16:23:20 +13:00
|
|
|
preComments: [],
|
|
|
|
commentStart: 0,
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2025-04-15 23:27:50 +10:00
|
|
|
_lastAst: Node<Program> = {
|
|
|
|
body: [],
|
|
|
|
shebang: null,
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
|
|
|
moduleId: 0,
|
|
|
|
nonCodeMeta: {
|
|
|
|
nonCodeNodes: {},
|
|
|
|
startNodes: [],
|
|
|
|
},
|
|
|
|
innerAttrs: [],
|
|
|
|
outerAttrs: [],
|
|
|
|
preComments: [],
|
|
|
|
commentStart: 0,
|
|
|
|
}
|
2024-10-09 19:38:40 -04:00
|
|
|
private _execState: ExecState = emptyExecState()
|
2025-02-12 10:22:56 +13:00
|
|
|
private _variables: VariableMap = {}
|
|
|
|
lastSuccessfulVariables: VariableMap = {}
|
2024-12-20 16:19:59 -05:00
|
|
|
lastSuccessfulOperations: Operation[] = []
|
2023-10-11 13:36:54 +11:00
|
|
|
private _logs: string[] = []
|
2024-12-20 16:19:59 -05:00
|
|
|
private _errors: KCLError[] = []
|
2024-12-06 13:57:31 +13:00
|
|
|
private _diagnostics: Diagnostic[] = []
|
2023-10-11 13:36:54 +11:00
|
|
|
private _isExecuting = false
|
2024-08-14 08:49:00 -07:00
|
|
|
private _executeIsStale: ExecuteArgs | null = null
|
2023-10-17 12:07:48 +11:00
|
|
|
private _wasmInitFailed = true
|
2025-03-21 11:44:44 +01:00
|
|
|
private _astParseFailed = false
|
2024-12-06 14:56:53 -08:00
|
|
|
private _switchedFiles = false
|
2025-02-06 12:37:13 -05:00
|
|
|
private _fileSettings: KclSettingsAnnotation = {}
|
2025-02-12 15:55:34 -08:00
|
|
|
private _kclVersion: string | undefined = undefined
|
2025-04-07 07:08:31 -04:00
|
|
|
private singletons: Singletons
|
2023-10-11 13:36:54 +11:00
|
|
|
|
|
|
|
engineCommandManager: EngineCommandManager
|
|
|
|
|
2023-10-17 12:07:48 +11:00
|
|
|
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
2024-10-30 16:52:17 -04:00
|
|
|
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
2025-04-16 00:11:25 +10:00
|
|
|
private _variablesCallBack: (
|
|
|
|
arg: {
|
|
|
|
[key in string]?: KclValue | undefined
|
|
|
|
}
|
|
|
|
) => void = () => {}
|
2023-10-11 13:36:54 +11:00
|
|
|
private _logsCallBack: (arg: string[]) => void = () => {}
|
2024-12-20 16:19:59 -05:00
|
|
|
private _kclErrorsCallBack: (errors: KCLError[]) => void = () => {}
|
|
|
|
private _diagnosticsCallback: (errors: Diagnostic[]) => void = () => {}
|
2023-10-17 12:07:48 +11:00
|
|
|
private _wasmInitFailedCallback: (arg: boolean) => void = () => {}
|
2025-04-03 07:31:18 +11:00
|
|
|
sceneInfraBaseUnitMultiplierSetter: (unit: BaseUnit) => void = () => {}
|
2023-10-11 13:36:54 +11:00
|
|
|
|
|
|
|
get ast() {
|
|
|
|
return this._ast
|
|
|
|
}
|
|
|
|
set ast(ast) {
|
2025-04-15 23:27:50 +10:00
|
|
|
if (this._ast.body.length !== 0) {
|
|
|
|
// last intact ast, if the user makes a typo with a syntax error, we want to keep the one before they made that mistake
|
|
|
|
this._lastAst = structuredClone(this._ast)
|
|
|
|
}
|
2023-10-11 13:36:54 +11:00
|
|
|
this._ast = ast
|
|
|
|
this._astCallBack(ast)
|
|
|
|
}
|
|
|
|
|
2024-12-06 14:56:53 -08:00
|
|
|
set switchedFiles(switchedFiles: boolean) {
|
|
|
|
this._switchedFiles = switchedFiles
|
|
|
|
}
|
|
|
|
|
2025-02-12 10:22:56 +13:00
|
|
|
get variables() {
|
|
|
|
return this._variables
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2024-10-09 19:38:40 -04:00
|
|
|
// This is private because callers should be setting the entire execState.
|
2025-02-12 10:22:56 +13:00
|
|
|
private set variables(variables) {
|
|
|
|
this._variables = variables
|
|
|
|
this._variablesCallBack(variables)
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
|
|
|
|
2024-12-05 19:51:06 -08:00
|
|
|
private set execState(execState) {
|
2024-10-09 19:38:40 -04:00
|
|
|
this._execState = execState
|
2025-02-12 10:22:56 +13:00
|
|
|
this.variables = execState.variables
|
2024-10-09 19:38:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
get execState() {
|
|
|
|
return this._execState
|
|
|
|
}
|
|
|
|
|
2025-02-12 15:55:34 -08:00
|
|
|
// Get the kcl version from the wasm module
|
|
|
|
// and store it in the singleton
|
|
|
|
// so we don't waste time getting it multiple times
|
|
|
|
get kclVersion() {
|
|
|
|
if (this._kclVersion === undefined) {
|
|
|
|
this._kclVersion = getKclVersion()
|
|
|
|
}
|
|
|
|
return this._kclVersion
|
|
|
|
}
|
|
|
|
|
2024-12-20 16:19:59 -05:00
|
|
|
get errors() {
|
|
|
|
return this._errors
|
|
|
|
}
|
|
|
|
set errors(errors) {
|
|
|
|
this._errors = errors
|
|
|
|
this._kclErrorsCallBack(errors)
|
|
|
|
}
|
2023-10-11 13:36:54 +11:00
|
|
|
get logs() {
|
|
|
|
return this._logs
|
|
|
|
}
|
|
|
|
set logs(logs) {
|
|
|
|
this._logs = logs
|
|
|
|
this._logsCallBack(logs)
|
|
|
|
}
|
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
get diagnostics() {
|
|
|
|
return this._diagnostics
|
2024-07-29 08:41:02 -07:00
|
|
|
}
|
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
set diagnostics(ds) {
|
|
|
|
if (ds === this._diagnostics) return
|
|
|
|
this._diagnostics = ds
|
|
|
|
this.setDiagnosticsForCurrentErrors()
|
2024-07-29 08:41:02 -07:00
|
|
|
}
|
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
addDiagnostics(ds: Diagnostic[]) {
|
|
|
|
if (ds.length === 0) return
|
|
|
|
this.diagnostics = this.diagnostics.concat(ds)
|
2024-08-04 15:16:34 -07:00
|
|
|
}
|
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
hasErrors(): boolean {
|
2025-03-21 11:44:44 +01:00
|
|
|
return this._astParseFailed || this._errors.length > 0
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
setDiagnosticsForCurrentErrors() {
|
2025-04-07 07:08:31 -04:00
|
|
|
this.singletons.editorManager?.setDiagnostics(this.diagnostics)
|
2024-12-20 16:19:59 -05:00
|
|
|
this._diagnosticsCallback(this.diagnostics)
|
2024-07-29 08:41:02 -07:00
|
|
|
}
|
|
|
|
|
2023-10-11 13:36:54 +11:00
|
|
|
get isExecuting() {
|
|
|
|
return this._isExecuting
|
|
|
|
}
|
2024-08-30 05:14:24 -05:00
|
|
|
|
2023-10-11 13:36:54 +11:00
|
|
|
set isExecuting(isExecuting) {
|
|
|
|
this._isExecuting = isExecuting
|
2024-08-14 08:49:00 -07:00
|
|
|
// If we have finished executing, but the execute is stale, we should
|
|
|
|
// execute again.
|
|
|
|
if (!isExecuting && this.executeIsStale) {
|
|
|
|
const args = this.executeIsStale
|
|
|
|
this.executeIsStale = null
|
2024-09-09 18:17:45 -04:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
2024-08-14 08:49:00 -07:00
|
|
|
this.executeAst(args)
|
|
|
|
}
|
2023-10-11 13:36:54 +11:00
|
|
|
this._isExecutingCallback(isExecuting)
|
|
|
|
}
|
|
|
|
|
2024-08-14 08:49:00 -07:00
|
|
|
get executeIsStale() {
|
|
|
|
return this._executeIsStale
|
|
|
|
}
|
|
|
|
|
|
|
|
set executeIsStale(executeIsStale) {
|
|
|
|
this._executeIsStale = executeIsStale
|
|
|
|
}
|
|
|
|
|
2023-10-17 12:07:48 +11:00
|
|
|
get wasmInitFailed() {
|
|
|
|
return this._wasmInitFailed
|
|
|
|
}
|
|
|
|
set wasmInitFailed(wasmInitFailed) {
|
|
|
|
this._wasmInitFailed = wasmInitFailed
|
|
|
|
this._wasmInitFailedCallback(wasmInitFailed)
|
|
|
|
}
|
|
|
|
|
2025-04-07 07:08:31 -04:00
|
|
|
constructor(
|
|
|
|
engineCommandManager: EngineCommandManager,
|
|
|
|
singletons: Singletons
|
|
|
|
) {
|
2023-10-11 13:36:54 +11:00
|
|
|
this.engineCommandManager = engineCommandManager
|
2025-04-07 07:08:31 -04:00
|
|
|
this.singletons = singletons
|
2023-11-06 11:49:13 +11:00
|
|
|
|
2024-09-09 18:17:45 -04:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
2024-12-06 14:56:53 -08:00
|
|
|
this.ensureWasmInit().then(async () => {
|
2025-04-07 07:08:31 -04:00
|
|
|
await this.safeParse(this.singletons.codeManager.code).then((ast) => {
|
2024-12-06 14:56:53 -08:00
|
|
|
if (ast) {
|
|
|
|
this.ast = ast
|
2025-04-15 23:27:50 +10:00
|
|
|
// on setup, set _lastAst so it's populated.
|
|
|
|
this._lastAst = ast
|
2024-12-06 14:56:53 -08:00
|
|
|
}
|
|
|
|
})
|
2024-02-11 12:59:00 +11:00
|
|
|
})
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2024-04-17 20:18:07 -07:00
|
|
|
|
2023-10-11 13:36:54 +11:00
|
|
|
registerCallBacks({
|
2025-02-12 10:22:56 +13:00
|
|
|
setVariables,
|
2023-10-11 13:36:54 +11:00
|
|
|
setAst,
|
|
|
|
setLogs,
|
2024-12-20 16:19:59 -05:00
|
|
|
setErrors,
|
|
|
|
setDiagnostics,
|
2023-10-11 13:36:54 +11:00
|
|
|
setIsExecuting,
|
2023-10-17 12:07:48 +11:00
|
|
|
setWasmInitFailed,
|
2023-10-11 13:36:54 +11:00
|
|
|
}: {
|
2025-02-12 10:22:56 +13:00
|
|
|
setVariables: (arg: VariableMap) => void
|
2024-10-30 16:52:17 -04:00
|
|
|
setAst: (arg: Node<Program>) => void
|
2023-10-11 13:36:54 +11:00
|
|
|
setLogs: (arg: string[]) => void
|
2024-12-20 16:19:59 -05:00
|
|
|
setErrors: (errors: KCLError[]) => void
|
|
|
|
setDiagnostics: (errors: Diagnostic[]) => void
|
2023-10-11 13:36:54 +11:00
|
|
|
setIsExecuting: (arg: boolean) => void
|
2023-10-17 12:07:48 +11:00
|
|
|
setWasmInitFailed: (arg: boolean) => void
|
2023-10-11 13:36:54 +11:00
|
|
|
}) {
|
2025-02-12 10:22:56 +13:00
|
|
|
this._variablesCallBack = setVariables
|
2023-10-11 13:36:54 +11:00
|
|
|
this._astCallBack = setAst
|
|
|
|
this._logsCallBack = setLogs
|
2024-12-20 16:19:59 -05:00
|
|
|
this._kclErrorsCallBack = setErrors
|
|
|
|
this._diagnosticsCallback = setDiagnostics
|
2023-10-11 13:36:54 +11:00
|
|
|
this._isExecutingCallback = setIsExecuting
|
2023-10-17 12:07:48 +11:00
|
|
|
this._wasmInitFailedCallback = setWasmInitFailed
|
|
|
|
}
|
|
|
|
|
2024-06-19 16:15:22 -04:00
|
|
|
clearAst() {
|
|
|
|
this._ast = {
|
|
|
|
body: [],
|
2024-11-26 16:39:57 +13:00
|
|
|
shebang: null,
|
2024-06-19 16:15:22 -04:00
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-11-07 11:23:41 -05:00
|
|
|
moduleId: 0,
|
2024-06-19 16:15:22 -04:00
|
|
|
nonCodeMeta: {
|
|
|
|
nonCodeNodes: {},
|
2024-10-30 16:52:17 -04:00
|
|
|
startNodes: [],
|
2024-06-19 16:15:22 -04:00
|
|
|
},
|
2025-02-13 16:17:09 +13:00
|
|
|
innerAttrs: [],
|
|
|
|
outerAttrs: [],
|
2025-03-20 16:23:20 +13:00
|
|
|
preComments: [],
|
|
|
|
commentStart: 0,
|
2024-06-19 16:15:22 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 14:56:53 -08:00
|
|
|
// (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.
|
2025-03-21 11:44:44 +01:00
|
|
|
if (this._astParseFailed && this._switchedFiles) {
|
2025-04-07 07:08:31 -04:00
|
|
|
await this.singletons.rustContext.clearSceneAndBustCache(
|
2025-04-30 12:00:37 -07:00
|
|
|
await jsAppSettings(),
|
2025-04-07 07:08:31 -04:00
|
|
|
this.singletons.codeManager.currentFilePath || undefined
|
2025-03-15 10:08:39 -07:00
|
|
|
)
|
2024-12-06 14:56:53 -08:00
|
|
|
} else if (this._switchedFiles) {
|
|
|
|
// Reset the switched files boolean.
|
|
|
|
this._switchedFiles = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-29 17:25:26 -07:00
|
|
|
private async updateArtifactGraph(
|
|
|
|
execStateArtifactGraph: ExecState['artifactGraph']
|
|
|
|
) {
|
|
|
|
this.artifactGraph = execStateArtifactGraph
|
|
|
|
this.artifactIndex = buildArtifactIndex(execStateArtifactGraph)
|
|
|
|
if (this.artifactGraph.size) {
|
|
|
|
// TODO: we wanna remove this logic from xstate, it is racey
|
|
|
|
// This defer is bullshit but playwright wants it
|
|
|
|
// It was like this in engineConnection.ts already
|
|
|
|
deferExecution((a?: null) => {
|
|
|
|
this.engineCommandManager.modelingSend({
|
|
|
|
type: 'Artifact graph emptied',
|
|
|
|
})
|
|
|
|
}, 200)(null)
|
|
|
|
} else {
|
|
|
|
deferExecution((a?: null) => {
|
|
|
|
this.engineCommandManager.modelingSend({
|
|
|
|
type: 'Artifact graph populated',
|
|
|
|
})
|
|
|
|
}, 200)(null)
|
|
|
|
}
|
2025-04-25 18:21:19 +02:00
|
|
|
|
|
|
|
// Send the 'artifact graph initialized' event for modelingMachine, only once, when default planes are also initialized.
|
|
|
|
deferExecution((a?: null) => {
|
|
|
|
if (this.defaultPlanes) {
|
|
|
|
this.engineCommandManager.modelingSend({
|
|
|
|
type: 'Artifact graph initialized',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}, 200)(null)
|
2025-03-29 17:25:26 -07:00
|
|
|
}
|
|
|
|
|
2024-12-06 14:56:53 -08:00
|
|
|
async safeParse(code: string): Promise<Node<Program> | null> {
|
2024-12-06 13:57:31 +13:00
|
|
|
const result = parse(code)
|
|
|
|
this.diagnostics = []
|
2025-03-21 11:44:44 +01:00
|
|
|
this._astParseFailed = false
|
2024-12-06 13:57:31 +13:00
|
|
|
|
|
|
|
if (err(result)) {
|
2025-03-25 05:06:27 -04:00
|
|
|
const kclError: KCLError = result as KCLError
|
|
|
|
this.diagnostics = kclErrorsToDiagnostics([kclError])
|
2025-03-21 11:44:44 +01:00
|
|
|
this._astParseFailed = true
|
2024-12-06 14:56:53 -08:00
|
|
|
|
|
|
|
await this.checkIfSwitchedFilesShouldClear()
|
2024-12-06 13:57:31 +13:00
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
2025-02-25 13:18:59 -06:00
|
|
|
// GOTCHA:
|
|
|
|
// When we safeParse this is tied to execution because they clicked a new file to load
|
|
|
|
// Clear all previous errors and logs because they are old since they executed a new file
|
|
|
|
// If we decouple safeParse from execution we need to move this application logic.
|
|
|
|
this._kclErrorsCallBack([])
|
|
|
|
this._logsCallBack([])
|
|
|
|
|
2025-03-25 05:06:27 -04:00
|
|
|
this.addDiagnostics(compilationErrorsToDiagnostics(result.errors))
|
|
|
|
this.addDiagnostics(compilationErrorsToDiagnostics(result.warnings))
|
2024-12-06 13:57:31 +13:00
|
|
|
if (result.errors.length > 0) {
|
2025-03-21 11:44:44 +01:00
|
|
|
this._astParseFailed = true
|
2024-12-06 13:57:31 +13:00
|
|
|
|
2024-12-06 14:56:53 -08:00
|
|
|
await this.checkIfSwitchedFilesShouldClear()
|
2024-12-06 13:57:31 +13:00
|
|
|
return null
|
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
return result.program
|
2023-11-06 17:47:45 +11:00
|
|
|
}
|
|
|
|
|
2023-10-17 12:07:48 +11:00
|
|
|
async ensureWasmInit() {
|
|
|
|
try {
|
|
|
|
await initPromise
|
|
|
|
if (this.wasmInitFailed) {
|
|
|
|
this.wasmInitFailed = false
|
|
|
|
}
|
2025-03-19 03:52:10 +11:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
2023-10-17 12:07:48 +11:00
|
|
|
} catch (e) {
|
2025-03-29 17:25:26 -07:00
|
|
|
console.error(e)
|
2023-10-17 12:07:48 +11:00
|
|
|
this.wasmInitFailed = true
|
|
|
|
}
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
|
|
|
|
2024-02-19 09:23:18 +11:00
|
|
|
private _cancelTokens: Map<number, boolean> = new Map()
|
|
|
|
|
2024-04-17 20:18:07 -07:00
|
|
|
// This NEVER updates the code, if you want to update the code DO NOT add to
|
2025-03-25 05:06:27 -04:00
|
|
|
// this function, too many other things that don't want it exist. For that,
|
|
|
|
// use updateModelingState().
|
2024-08-14 08:49:00 -07:00
|
|
|
async executeAst(args: ExecuteArgs = {}): Promise<void> {
|
|
|
|
if (this.isExecuting) {
|
|
|
|
this.executeIsStale = args
|
2024-08-30 05:14:24 -05:00
|
|
|
|
2025-03-17 18:26:11 -07:00
|
|
|
// The previous executeAst will be rejected and cleaned up. The execution will be marked as stale.
|
2024-08-30 05:14:24 -05:00
|
|
|
// A new executeAst will start.
|
|
|
|
this.engineCommandManager.rejectAllModelingCommands(
|
|
|
|
EXECUTE_AST_INTERRUPT_ERROR_MESSAGE
|
|
|
|
)
|
2024-08-14 08:49:00 -07:00
|
|
|
// Exit early if we are already executing.
|
|
|
|
return
|
2024-07-04 01:19:24 -04:00
|
|
|
}
|
2024-08-14 08:49:00 -07:00
|
|
|
|
|
|
|
const ast = args.ast || this.ast
|
2024-11-07 17:23:03 -05:00
|
|
|
markOnce('code/startExecuteAst')
|
2024-08-14 08:49:00 -07:00
|
|
|
|
|
|
|
const currentExecutionId = args.executionId || Date.now()
|
2024-02-19 09:23:18 +11:00
|
|
|
this._cancelTokens.set(currentExecutionId, false)
|
|
|
|
|
2023-10-11 13:36:54 +11:00
|
|
|
this.isExecuting = true
|
2024-02-26 21:02:33 +11:00
|
|
|
await this.ensureWasmInit()
|
2024-10-09 19:38:40 -04:00
|
|
|
const { logs, errors, execState, isInterrupted } = await executeAst({
|
2023-10-11 13:36:54 +11:00
|
|
|
ast,
|
2025-04-07 07:08:31 -04:00
|
|
|
path: this.singletons.codeManager.currentFilePath || undefined,
|
|
|
|
rustContext: this.singletons.rustContext,
|
2023-10-11 13:36:54 +11:00
|
|
|
})
|
2024-06-26 00:04:52 -04:00
|
|
|
|
2024-08-30 05:14:24 -05:00
|
|
|
// Program was not interrupted, setup the scene
|
|
|
|
// Do not send send scene commands if the program was interrupted, go to clean up
|
|
|
|
if (!isInterrupted) {
|
2024-12-06 13:57:31 +13:00
|
|
|
this.addDiagnostics(await lintAst({ ast: ast }))
|
2025-03-29 17:25:26 -07:00
|
|
|
await setSelectionFilterToDefault(this.engineCommandManager)
|
2024-05-24 12:32:15 -07:00
|
|
|
}
|
|
|
|
|
2023-10-11 13:36:54 +11:00
|
|
|
this.isExecuting = false
|
2024-08-14 08:49:00 -07:00
|
|
|
|
2024-02-19 09:23:18 +11:00
|
|
|
// Check the cancellation token for this execution before applying side effects
|
|
|
|
if (this._cancelTokens.get(currentExecutionId)) {
|
|
|
|
this._cancelTokens.delete(currentExecutionId)
|
|
|
|
return
|
|
|
|
}
|
2024-10-09 10:33:20 -04:00
|
|
|
|
2025-02-06 12:37:13 -05:00
|
|
|
let fileSettings = getSettingsAnnotation(ast)
|
|
|
|
if (err(fileSettings)) {
|
|
|
|
console.error(fileSettings)
|
|
|
|
fileSettings = {}
|
|
|
|
}
|
|
|
|
this.fileSettings = fileSettings
|
|
|
|
|
2023-11-08 12:27:43 +11:00
|
|
|
this.logs = logs
|
2024-12-20 16:19:59 -05:00
|
|
|
this.errors = errors
|
2024-08-30 05:14:24 -05:00
|
|
|
// Do not add the errors since the program was interrupted and the error is not a real KCL error
|
2024-12-06 13:57:31 +13:00
|
|
|
this.addDiagnostics(isInterrupted ? [] : kclErrorsToDiagnostics(errors))
|
2025-02-21 09:30:44 +13:00
|
|
|
// Add warnings and non-fatal errors
|
|
|
|
this.addDiagnostics(
|
2025-03-25 05:06:27 -04:00
|
|
|
isInterrupted ? [] : compilationErrorsToDiagnostics(execState.errors)
|
2025-02-21 09:30:44 +13:00
|
|
|
)
|
2024-10-09 19:38:40 -04:00
|
|
|
this.execState = execState
|
2024-10-02 13:15:40 +10:00
|
|
|
if (!errors.length) {
|
2025-02-12 10:22:56 +13:00
|
|
|
this.lastSuccessfulVariables = execState.variables
|
2024-12-20 16:19:59 -05:00
|
|
|
this.lastSuccessfulOperations = execState.operations
|
2024-10-02 13:15:40 +10:00
|
|
|
}
|
2025-04-15 23:27:50 +10:00
|
|
|
this.ast = structuredClone(ast)
|
2025-02-12 10:22:56 +13:00
|
|
|
// updateArtifactGraph relies on updated executeState/variables
|
2025-03-29 17:25:26 -07:00
|
|
|
await this.updateArtifactGraph(execState.artifactGraph)
|
2024-12-16 10:34:11 -05:00
|
|
|
if (!isInterrupted) {
|
2025-04-07 07:08:31 -04:00
|
|
|
this.singletons.sceneInfra.modelingSend({
|
|
|
|
type: 'code edit during sketch',
|
|
|
|
})
|
2024-12-16 10:34:11 -05:00
|
|
|
}
|
2024-03-22 16:55:30 +11:00
|
|
|
this.engineCommandManager.addCommandLog({
|
2025-04-07 07:08:31 -04:00
|
|
|
type: CommandLogType.ExecutionDone,
|
2023-11-24 08:59:24 +11:00
|
|
|
data: null,
|
|
|
|
})
|
2024-08-30 05:14:24 -05:00
|
|
|
|
2024-02-19 09:23:18 +11:00
|
|
|
this._cancelTokens.delete(currentExecutionId)
|
2024-11-07 17:23:03 -05:00
|
|
|
markOnce('code/endExecuteAst')
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2025-01-08 10:58:41 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This cleanup function is external and internal to the KclSingleton class.
|
|
|
|
* Since the WASM runtime can panic and the error cannot be caught in executeAst
|
|
|
|
* we need a global exception handler in exceptions.ts
|
|
|
|
* This file will interface with this cleanup as if it caught the original error
|
|
|
|
* to properly restore the TS application state.
|
|
|
|
*/
|
|
|
|
executeAstCleanUp() {
|
|
|
|
this.isExecuting = false
|
|
|
|
this.executeIsStale = null
|
|
|
|
this.engineCommandManager.addCommandLog({
|
2025-04-07 07:08:31 -04:00
|
|
|
type: CommandLogType.ExecutionDone,
|
2025-01-08 10:58:41 -05:00
|
|
|
data: null,
|
|
|
|
})
|
|
|
|
markOnce('code/endExecuteAst')
|
|
|
|
}
|
|
|
|
|
2024-04-17 20:18:07 -07:00
|
|
|
// DO NOT CALL THIS from codemirror ever.
|
2025-04-12 00:16:45 +10:00
|
|
|
async executeAstMock(ast: Program): Promise<null | Error> {
|
2023-10-17 12:07:48 +11:00
|
|
|
await this.ensureWasmInit()
|
2024-06-11 19:23:35 -04:00
|
|
|
|
2023-10-11 13:36:54 +11:00
|
|
|
const newCode = recast(ast)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(newCode)) {
|
|
|
|
console.error(newCode)
|
2025-04-12 00:16:45 +10:00
|
|
|
return newCode
|
2024-06-24 11:45:40 -04:00
|
|
|
}
|
2024-12-06 14:56:53 -08:00
|
|
|
const newAst = await this.safeParse(newCode)
|
2025-04-12 00:16:45 +10:00
|
|
|
|
2024-06-26 00:04:52 -04:00
|
|
|
if (!newAst) {
|
2025-03-25 05:06:27 -04:00
|
|
|
// By clearing the AST we indicate to our callers that there was an issue with execution and
|
2025-03-09 03:33:45 +13:00
|
|
|
// the pre-execution state should be restored.
|
2024-06-26 00:04:52 -04:00
|
|
|
this.clearAst()
|
2025-04-12 00:16:45 +10:00
|
|
|
return new Error('failed to re-parse')
|
2024-06-26 00:04:52 -04:00
|
|
|
}
|
2023-10-11 13:36:54 +11:00
|
|
|
this._ast = { ...newAst }
|
|
|
|
|
2025-03-17 18:26:11 -07:00
|
|
|
const { logs, errors, execState } = await executeAstMock({
|
2023-10-11 13:36:54 +11:00
|
|
|
ast: newAst,
|
2025-04-07 07:08:31 -04:00
|
|
|
rustContext: this.singletons.rustContext,
|
2023-10-11 13:36:54 +11:00
|
|
|
})
|
2024-06-26 00:04:52 -04:00
|
|
|
|
2023-10-11 13:36:54 +11:00
|
|
|
this._logs = logs
|
2024-10-09 19:38:40 -04:00
|
|
|
this._execState = execState
|
2025-02-12 10:22:56 +13:00
|
|
|
this._variables = execState.variables
|
2024-10-02 13:15:40 +10:00
|
|
|
if (!errors.length) {
|
2025-02-12 10:22:56 +13:00
|
|
|
this.lastSuccessfulVariables = execState.variables
|
2024-12-20 16:19:59 -05:00
|
|
|
this.lastSuccessfulOperations = execState.operations
|
2024-10-02 13:15:40 +10:00
|
|
|
}
|
2025-04-12 00:16:45 +10:00
|
|
|
return null
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2024-02-19 09:23:18 +11:00
|
|
|
cancelAllExecutions() {
|
|
|
|
this._cancelTokens.forEach((_, key) => {
|
|
|
|
this._cancelTokens.set(key, true)
|
|
|
|
})
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2025-04-03 10:58:18 -05:00
|
|
|
async executeCode(): Promise<void> {
|
2025-04-07 07:08:31 -04:00
|
|
|
const ast = await this.safeParse(this.singletons.codeManager.code)
|
2025-01-06 21:53:20 -05:00
|
|
|
|
2024-06-19 16:15:22 -04:00
|
|
|
if (!ast) {
|
2025-03-25 05:06:27 -04:00
|
|
|
// By clearing the AST we indicate to our callers that there was an issue with execution and
|
2025-03-09 03:33:45 +13:00
|
|
|
// the pre-execution state should be restored.
|
2024-06-19 16:15:22 -04:00
|
|
|
this.clearAst()
|
|
|
|
return
|
|
|
|
}
|
2025-01-06 21:53:20 -05:00
|
|
|
|
2025-04-15 23:27:50 +10:00
|
|
|
return this.executeAst({ ast })
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2025-01-06 21:53:20 -05:00
|
|
|
|
2024-12-06 14:56:53 -08:00
|
|
|
async format() {
|
2025-04-07 07:08:31 -04:00
|
|
|
const originalCode = this.singletons.codeManager.code
|
2024-12-06 14:56:53 -08:00
|
|
|
const ast = await this.safeParse(originalCode)
|
2024-06-19 16:15:22 -04:00
|
|
|
if (!ast) {
|
|
|
|
this.clearAst()
|
|
|
|
return
|
|
|
|
}
|
2024-04-17 20:18:07 -07:00
|
|
|
const code = recast(ast)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(code)) {
|
|
|
|
console.error(code)
|
|
|
|
return
|
|
|
|
}
|
2024-04-17 20:18:07 -07:00
|
|
|
if (originalCode === code) return
|
|
|
|
|
|
|
|
// Update the code state and the editor.
|
2025-04-07 07:08:31 -04:00
|
|
|
this.singletons.codeManager.updateCodeStateEditor(code)
|
2024-11-08 17:16:46 -05:00
|
|
|
|
2024-11-16 16:49:44 -05:00
|
|
|
// Write back to the file system.
|
2025-04-07 07:08:31 -04:00
|
|
|
void this.singletons.codeManager
|
2025-03-25 05:06:27 -04:00
|
|
|
.writeToFile()
|
|
|
|
.then(() => this.executeCode())
|
|
|
|
.catch(reportRejection)
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2025-03-25 05:06:27 -04:00
|
|
|
|
2023-11-01 17:34:54 -05:00
|
|
|
// There's overlapping responsibility between updateAst and executeAst.
|
2023-10-11 13:36:54 +11:00
|
|
|
// 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
|
2025-03-25 05:06:27 -04:00
|
|
|
// This never updates the code state or editor and doesn't write to the file system.
|
2023-10-11 13:36:54 +11:00
|
|
|
async updateAst(
|
2024-10-30 16:52:17 -04:00
|
|
|
ast: Node<Program>,
|
2023-10-11 13:36:54 +11:00
|
|
|
execute: boolean,
|
|
|
|
optionalParams?: {
|
2024-09-23 08:07:31 +02:00
|
|
|
focusPath?: Array<PathToNode>
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
): Promise<{
|
2024-10-30 16:52:17 -04:00
|
|
|
newAst: Node<Program>
|
2024-06-24 11:45:40 -04:00
|
|
|
selections?: Selections
|
|
|
|
}> {
|
2023-10-11 13:36:54 +11:00
|
|
|
const newCode = recast(ast)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(newCode)) return Promise.reject(newCode)
|
|
|
|
|
2024-12-06 14:56:53 -08:00
|
|
|
const astWithUpdatedSource = await this.safeParse(newCode)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (!astWithUpdatedSource) return Promise.reject(new Error('bad ast'))
|
|
|
|
let returnVal: Selections | undefined = undefined
|
2023-10-11 13:36:54 +11:00
|
|
|
|
|
|
|
if (optionalParams?.focusPath) {
|
|
|
|
returnVal = {
|
2024-11-21 15:04:30 +11:00
|
|
|
graphSelections: [],
|
2024-09-23 08:07:31 +02:00
|
|
|
otherSelections: [],
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const path of optionalParams.focusPath) {
|
|
|
|
const getNodeFromPathResult = getNodeFromPath<any>(
|
|
|
|
astWithUpdatedSource,
|
|
|
|
path
|
|
|
|
)
|
|
|
|
if (err(getNodeFromPathResult))
|
|
|
|
return Promise.reject(getNodeFromPathResult)
|
|
|
|
const { node } = getNodeFromPathResult
|
|
|
|
|
|
|
|
const { start, end } = node
|
|
|
|
|
|
|
|
if (!start || !end)
|
|
|
|
return {
|
|
|
|
selections: undefined,
|
|
|
|
newAst: astWithUpdatedSource,
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start && end) {
|
2024-11-21 15:04:30 +11:00
|
|
|
returnVal.graphSelections.push({
|
|
|
|
codeRef: {
|
2025-01-17 14:34:36 -05:00
|
|
|
range: topLevelRange(start, end),
|
2024-11-21 15:04:30 +11:00
|
|
|
pathToNode: path,
|
|
|
|
},
|
2024-09-23 08:07:31 +02:00
|
|
|
})
|
|
|
|
}
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (execute) {
|
2024-08-14 08:49:00 -07:00
|
|
|
await this.executeAst({
|
|
|
|
ast: astWithUpdatedSource,
|
|
|
|
})
|
2023-10-11 13:36:54 +11:00
|
|
|
} 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
|
2024-04-17 20:18:07 -07:00
|
|
|
// instead..
|
2025-04-12 00:16:45 +10:00
|
|
|
const didReParse = await this.executeAstMock(astWithUpdatedSource)
|
|
|
|
if (err(didReParse)) return Promise.reject(didReParse)
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
|
|
|
|
return { selections: returnVal, newAst: astWithUpdatedSource }
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2024-02-17 07:04:24 +11:00
|
|
|
|
|
|
|
get defaultPlanes() {
|
2025-04-07 07:08:31 -04:00
|
|
|
return this.singletons.rustContext.defaultPlanes
|
2024-02-17 07:04:24 +11:00
|
|
|
}
|
|
|
|
|
2024-06-18 16:08:41 +10:00
|
|
|
showPlanes(all = false) {
|
|
|
|
if (!this.defaultPlanes) return Promise.all([])
|
|
|
|
const thePromises = [
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.xy, false),
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.yz, false),
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.xz, false),
|
|
|
|
]
|
|
|
|
if (all) {
|
|
|
|
thePromises.push(
|
|
|
|
this.engineCommandManager.setPlaneHidden(
|
|
|
|
this.defaultPlanes.negXy,
|
|
|
|
false
|
|
|
|
)
|
|
|
|
)
|
|
|
|
thePromises.push(
|
|
|
|
this.engineCommandManager.setPlaneHidden(
|
|
|
|
this.defaultPlanes.negYz,
|
|
|
|
false
|
|
|
|
)
|
|
|
|
)
|
|
|
|
thePromises.push(
|
|
|
|
this.engineCommandManager.setPlaneHidden(
|
|
|
|
this.defaultPlanes.negXz,
|
|
|
|
false
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return Promise.all(thePromises)
|
2024-02-17 07:04:24 +11:00
|
|
|
}
|
|
|
|
|
2024-06-18 16:08:41 +10:00
|
|
|
hidePlanes(all = false) {
|
|
|
|
if (!this.defaultPlanes) return Promise.all([])
|
|
|
|
const thePromises = [
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.xy, true),
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.yz, true),
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.xz, true),
|
|
|
|
]
|
|
|
|
if (all) {
|
|
|
|
thePromises.push(
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.negXy, true)
|
|
|
|
)
|
|
|
|
thePromises.push(
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.negYz, true)
|
|
|
|
)
|
|
|
|
thePromises.push(
|
|
|
|
this.engineCommandManager.setPlaneHidden(this.defaultPlanes.negXz, true)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return Promise.all(thePromises)
|
2024-02-17 07:04:24 +11:00
|
|
|
}
|
2025-04-25 18:21:19 +02:00
|
|
|
|
|
|
|
setPlaneVisibilityByKey(
|
|
|
|
planeKey: keyof PlaneVisibilityMap,
|
|
|
|
visible: boolean
|
|
|
|
) {
|
|
|
|
const planeId = this.defaultPlanes?.[planeKey]
|
|
|
|
if (!planeId) {
|
|
|
|
console.warn(`Plane ${planeKey} not found`)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return this.engineCommandManager.setPlaneHidden(planeId, !visible)
|
|
|
|
}
|
|
|
|
|
2024-11-26 11:36:14 -05:00
|
|
|
/** TODO: this function is hiding unawaited asynchronous work */
|
2024-12-20 13:39:06 +11:00
|
|
|
defaultSelectionFilter(selectionsToRestore?: Selections) {
|
|
|
|
setSelectionFilterToDefault(this.engineCommandManager, selectionsToRestore)
|
2024-11-26 11:36:14 -05:00
|
|
|
}
|
|
|
|
/** TODO: this function is hiding unawaited asynchronous work */
|
|
|
|
setSelectionFilter(filter: EntityType_type[]) {
|
|
|
|
setSelectionFilter(filter, this.engineCommandManager)
|
2024-05-21 05:55:34 +10:00
|
|
|
}
|
2024-10-09 10:33:20 -04:00
|
|
|
|
|
|
|
// Determines if there is no KCL code which means it is executing a blank KCL file
|
2024-10-30 16:52:17 -04:00
|
|
|
_isAstEmpty(ast: Node<Program>) {
|
2024-10-09 10:33:20 -04:00
|
|
|
return ast.start === 0 && ast.end === 0 && ast.body.length === 0
|
|
|
|
}
|
2025-02-06 12:37:13 -05:00
|
|
|
|
|
|
|
get fileSettings() {
|
|
|
|
return this._fileSettings
|
|
|
|
}
|
|
|
|
|
|
|
|
set fileSettings(settings: KclSettingsAnnotation) {
|
|
|
|
this._fileSettings = settings
|
2025-04-03 07:31:18 +11:00
|
|
|
this.sceneInfraBaseUnitMultiplierSetter(
|
|
|
|
settings?.defaultLengthUnit || DEFAULT_DEFAULT_LENGTH_UNIT
|
|
|
|
)
|
2025-02-06 12:37:13 -05:00
|
|
|
}
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
|
|
|
|
2024-11-26 11:36:14 -05:00
|
|
|
const defaultSelectionFilter: EntityType_type[] = [
|
|
|
|
'face',
|
|
|
|
'edge',
|
|
|
|
'solid2d',
|
|
|
|
'curve',
|
|
|
|
'object',
|
|
|
|
]
|
|
|
|
|
|
|
|
/** TODO: This function is not synchronous but is currently treated as such */
|
|
|
|
function setSelectionFilterToDefault(
|
2024-12-20 13:39:06 +11:00
|
|
|
engineCommandManager: EngineCommandManager,
|
|
|
|
selectionsToRestore?: Selections
|
2024-03-22 16:55:30 +11:00
|
|
|
) {
|
2024-09-09 18:17:45 -04:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
2024-12-20 13:39:06 +11:00
|
|
|
setSelectionFilter(
|
|
|
|
defaultSelectionFilter,
|
|
|
|
engineCommandManager,
|
|
|
|
selectionsToRestore
|
|
|
|
)
|
2024-11-26 11:36:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/** TODO: This function is not synchronous but is currently treated as such */
|
|
|
|
function setSelectionFilter(
|
|
|
|
filter: EntityType_type[],
|
2024-12-20 13:39:06 +11:00
|
|
|
engineCommandManager: EngineCommandManager,
|
|
|
|
selectionsToRestore?: Selections
|
2024-11-26 11:36:14 -05:00
|
|
|
) {
|
2024-12-20 13:39:06 +11:00
|
|
|
const { engineEvents } = selectionsToRestore
|
|
|
|
? handleSelectionBatch({
|
|
|
|
selections: selectionsToRestore,
|
|
|
|
})
|
|
|
|
: { engineEvents: undefined }
|
|
|
|
if (!selectionsToRestore || !engineEvents) {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
|
|
engineCommandManager.sendSceneCommand({
|
|
|
|
type: 'modeling_cmd_req',
|
|
|
|
cmd_id: uuidv4(),
|
|
|
|
cmd: {
|
|
|
|
type: 'set_selection_filter',
|
|
|
|
filter,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const modelingCmd: ModelingCmdReq_type[] = []
|
|
|
|
engineEvents.forEach((event) => {
|
|
|
|
if (event.type === 'modeling_cmd_req') {
|
|
|
|
modelingCmd.push({
|
|
|
|
cmd_id: uuidv4(),
|
|
|
|
cmd: event.cmd,
|
|
|
|
})
|
|
|
|
}
|
2024-11-26 11:36:14 -05:00
|
|
|
})
|
2024-12-20 13:39:06 +11:00
|
|
|
// batch is needed other wise the selection flickers.
|
|
|
|
engineCommandManager
|
|
|
|
.sendSceneCommand({
|
|
|
|
type: 'modeling_cmd_batch_req',
|
|
|
|
batch_id: uuidv4(),
|
|
|
|
requests: [
|
|
|
|
{
|
|
|
|
cmd_id: uuidv4(),
|
|
|
|
cmd: {
|
|
|
|
type: 'set_selection_filter',
|
|
|
|
filter,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
...modelingCmd,
|
|
|
|
],
|
|
|
|
responses: false,
|
|
|
|
})
|
|
|
|
.catch(reportError)
|
2024-03-22 10:23:04 +11:00
|
|
|
}
|