From 3471f7347900fa26ecc85b6c1479286ec55e1e65 Mon Sep 17 00:00:00 2001 From: lee-at-zoo-corp Date: Thu, 3 Apr 2025 14:32:04 -0400 Subject: [PATCH] Get rid of 1 cyclic dependency --- src/editor/manager.ts | 9 +++++--- src/lang/KclSingleton.ts | 50 ++++++++++++++++++++++++---------------- src/lib/singletons.ts | 30 ++++++++++++++++-------- 3 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/editor/manager.ts b/src/editor/manager.ts index 14e6400a9..7be9d6fbc 100644 --- a/src/editor/manager.ts +++ b/src/editor/manager.ts @@ -47,7 +47,6 @@ export const setDiagnosticsEvent = setDiagnosticsAnnotation.of(true) export default class EditorManager { private _copilotEnabled: boolean = true private engineCommandManager: EngineCommandManager - private kclManager: KclManager private _isAllTextSelected: boolean = false private _isShiftDown: boolean = false @@ -67,13 +66,12 @@ export default class EditorManager { private _highlightRange: Array<[number, number]> = [[0, 0]] public _editorView: EditorView | null = null + public kclManager?: KclManager constructor( engineCommandManager: EngineCommandManager, - kclManager: KclManager ) { this.engineCommandManager = engineCommandManager - this.kclManager = kclManager } setCopilotEnabled(enabled: boolean) { @@ -387,6 +385,11 @@ export default class EditorManager { } ) + if (!this.kclManager) { + console.error('unreachable') + return + } + const eventInfo = processCodeMirrorRanges({ codeMirrorRanges: viewUpdate.state.selection.ranges, selectionRanges: this._selectionRanges, diff --git a/src/lang/KclSingleton.ts b/src/lang/KclSingleton.ts index 5b4762ef1..f2ae389d7 100644 --- a/src/lang/KclSingleton.ts +++ b/src/lang/KclSingleton.ts @@ -1,3 +1,7 @@ +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' import type { Diagnostic } from '@codemirror/lint' import type { EntityType_type, @@ -47,12 +51,7 @@ import type { KclSettingsAnnotation, } from '@src/lib/settings/settingsTypes' import { jsAppSettings } from '@src/lib/settings/settingsUtils' -import { - codeManager, - editorManager, - rustContext, - sceneInfra, -} from '@src/lib/singletons' + import { err, reportRejection } from '@src/lib/trap' import { deferExecution, isOverlap, uuidv4 } from '@src/lib/utils' @@ -61,6 +60,15 @@ interface ExecuteArgs { executionId?: number } +// 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 +} + export class KclManager { /** * The artifactGraph is a client-side representation of the commands that have been sent @@ -99,6 +107,7 @@ export class KclManager { private _switchedFiles = false private _fileSettings: KclSettingsAnnotation = {} private _kclVersion: string | undefined = undefined + private singletons: Singletons engineCommandManager: EngineCommandManager @@ -189,7 +198,7 @@ export class KclManager { } setDiagnosticsForCurrentErrors() { - editorManager?.setDiagnostics(this.diagnostics) + this.singletons.editorManager?.setDiagnostics(this.diagnostics) this._diagnosticsCallback(this.diagnostics) } @@ -226,12 +235,13 @@ export class KclManager { this._wasmInitFailedCallback(wasmInitFailed) } - constructor(engineCommandManager: EngineCommandManager) { + constructor(engineCommandManager: EngineCommandManager, singletons: Singletons) { this.engineCommandManager = engineCommandManager + this.singletons = singletons // eslint-disable-next-line @typescript-eslint/no-floating-promises this.ensureWasmInit().then(async () => { - await this.safeParse(codeManager.code).then((ast) => { + await this.safeParse(this.singletons.codeManager.code).then((ast) => { if (ast) { this.ast = ast } @@ -297,9 +307,9 @@ export class KclManager { // If we were switching files and we hit an error on parse we need to bust // the cache and clear the scene. if (this._astParseFailed && this._switchedFiles) { - await rustContext.clearSceneAndBustCache( + await this.singletons.rustContext.clearSceneAndBustCache( { settings: await jsAppSettings() }, - codeManager.currentFilePath || undefined + this.singletons.codeManager.currentFilePath || undefined ) } else if (this._switchedFiles) { // Reset the switched files boolean. @@ -422,8 +432,8 @@ export class KclManager { await this.ensureWasmInit() const { logs, errors, execState, isInterrupted } = await executeAst({ ast, - path: codeManager.currentFilePath || undefined, - rustContext, + path: this.singletons.codeManager.currentFilePath || undefined, + rustContext: this.singletons.rustContext, }) // Program was not interrupted, setup the scene @@ -471,7 +481,7 @@ export class KclManager { await this.updateArtifactGraph(execState.artifactGraph) this._executeCallback() if (!isInterrupted) { - sceneInfra.modelingSend({ type: 'code edit during sketch' }) + this.singletons.sceneInfra.modelingSend({ type: 'code edit during sketch' }) } this.engineCommandManager.addCommandLog({ type: CommandLogType.ExecutionDone, @@ -519,7 +529,7 @@ export class KclManager { const { logs, errors, execState } = await executeAstMock({ ast: newAst, - rustContext, + rustContext: this.singletons.rustContext, }) this._logs = logs @@ -536,7 +546,7 @@ export class KclManager { }) } async executeCode(): Promise { - const ast = await this.safeParse(codeManager.code) + const ast = await this.safeParse(this.singletons.codeManager.code) if (!ast) { // By clearing the AST we indicate to our callers that there was an issue with execution and @@ -550,7 +560,7 @@ export class KclManager { } async format() { - const originalCode = codeManager.code + const originalCode = this.singletons.codeManager.code const ast = await this.safeParse(originalCode) if (!ast) { this.clearAst() @@ -564,10 +574,10 @@ export class KclManager { if (originalCode === code) return // Update the code state and the editor. - codeManager.updateCodeStateEditor(code) + this.singletons.codeManager.updateCodeStateEditor(code) // Write back to the file system. - void codeManager + void this.singletons.codeManager .writeToFile() .then(() => this.executeCode()) .catch(reportRejection) @@ -643,7 +653,7 @@ export class KclManager { } get defaultPlanes() { - return rustContext.defaultPlanes + return this.singletons.rustContext.defaultPlanes } showPlanes(all = false) { diff --git a/src/lib/singletons.ts b/src/lib/singletons.ts index 6b4de6ae6..1929cd9ab 100644 --- a/src/lib/singletons.ts +++ b/src/lib/singletons.ts @@ -10,8 +10,8 @@ import { SceneInfra } from '@src/clientSideScene/sceneInfra' import type { BaseUnit } from '@src/lib/settings/settingsTypes' export const codeManager = new CodeManager() - export const engineCommandManager = new EngineCommandManager() +export const rustContext = new RustContext(engineCommandManager) declare global { interface Window { @@ -23,21 +23,31 @@ declare global { // Accessible for tests mostly window.engineCommandManager = engineCommandManager -// This needs to be after codeManager is created. -export const kclManager = new KclManager(engineCommandManager) -engineCommandManager.kclManager = kclManager - export const sceneInfra = new SceneInfra(engineCommandManager) engineCommandManager.camControlsCameraChange = sceneInfra.onCameraChange + +// This needs to be after sceneInfra and engineCommandManager are is created. +export const editorManager = new EditorManager(engineCommandManager) + +// This needs to be after codeManager is created. +// (lee: what??? why?) +export const kclManager = new KclManager(engineCommandManager, { + rustContext, + codeManager, + editorManager, + sceneInfra, +}) + +// The most obvious of cyclic dependencies. +// This is because the handleOnViewUpdate(viewUpdate: ViewUpdate): void { +// method requires it for the current ast. +editorManager.kclManager = kclManager + +engineCommandManager.kclManager = kclManager kclManager.sceneInfraBaseUnitMultiplierSetter = (unit: BaseUnit) => { sceneInfra.baseUnit = unit } -// This needs to be after sceneInfra and engineCommandManager are is created. -export const editorManager = new EditorManager(engineCommandManager, kclManager) - -export const rustContext = new RustContext(engineCommandManager) - export const sceneEntitiesManager = new SceneEntities( engineCommandManager, sceneInfra,