UNPKG

monaco-editor

Version:
216 lines (213 loc) • 8.34 kB
import { assertFn } from '../../assert.js'; import '../../arrays.js'; import { onBugIndicatingError, BugIndicatingError } from '../../errors.js'; import '../../event.js'; import { DisposableStore } from '../../lifecycle.js'; import { getLogger } from '../logging/logging.js'; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ function autorunStateToString(state) { switch (state) { case 1 /* AutorunState.dependenciesMightHaveChanged */: return 'dependenciesMightHaveChanged'; case 2 /* AutorunState.stale */: return 'stale'; case 3 /* AutorunState.upToDate */: return 'upToDate'; default: return '<unknown>'; } } class AutorunObserver { get debugName() { return this._debugNameData.getDebugName(this) ?? '(anonymous)'; } constructor(_debugNameData, _runFn, _changeTracker, debugLocation) { this._debugNameData = _debugNameData; this._runFn = _runFn; this._changeTracker = _changeTracker; this._state = 2 /* AutorunState.stale */; this._updateCount = 0; this._disposed = false; this._dependencies = new Set(); this._dependenciesToBeRemoved = new Set(); this._isRunning = false; this._store = undefined; this._delayedStore = undefined; this._changeSummary = this._changeTracker?.createChangeSummary(undefined); getLogger()?.handleAutorunCreated(this, debugLocation); this._run(); } dispose() { if (this._disposed) { return; } this._disposed = true; for (const o of this._dependencies) { o.removeObserver(this); // Warning: external call! } this._dependencies.clear(); if (this._store !== undefined) { this._store.dispose(); } if (this._delayedStore !== undefined) { this._delayedStore.dispose(); } getLogger()?.handleAutorunDisposed(this); } _run() { const emptySet = this._dependenciesToBeRemoved; this._dependenciesToBeRemoved = this._dependencies; this._dependencies = emptySet; this._state = 3 /* AutorunState.upToDate */; try { if (!this._disposed) { getLogger()?.handleAutorunStarted(this); const changeSummary = this._changeSummary; const delayedStore = this._delayedStore; if (delayedStore !== undefined) { this._delayedStore = undefined; } try { this._isRunning = true; if (this._changeTracker) { this._changeTracker.beforeUpdate?.(this, changeSummary); this._changeSummary = this._changeTracker.createChangeSummary(changeSummary); // Warning: external call! } if (this._store !== undefined) { this._store.dispose(); this._store = undefined; } this._runFn(this, changeSummary); // Warning: external call! } catch (e) { onBugIndicatingError(e); } finally { this._isRunning = false; if (delayedStore !== undefined) { delayedStore.dispose(); } } } } finally { if (!this._disposed) { getLogger()?.handleAutorunFinished(this); } // We don't want our observed observables to think that they are (not even temporarily) not being observed. // Thus, we only unsubscribe from observables that are definitely not read anymore. for (const o of this._dependenciesToBeRemoved) { o.removeObserver(this); // Warning: external call! } this._dependenciesToBeRemoved.clear(); } } toString() { return `Autorun<${this.debugName}>`; } // IObserver implementation beginUpdate(_observable) { if (this._state === 3 /* AutorunState.upToDate */) { this._state = 1 /* AutorunState.dependenciesMightHaveChanged */; } this._updateCount++; } endUpdate(_observable) { try { if (this._updateCount === 1) { do { if (this._state === 1 /* AutorunState.dependenciesMightHaveChanged */) { this._state = 3 /* AutorunState.upToDate */; for (const d of this._dependencies) { d.reportChanges(); // Warning: external call! if (this._state === 2 /* AutorunState.stale */) { // The other dependencies will refresh on demand break; } } } if (this._state !== 3 /* AutorunState.upToDate */) { this._run(); // Warning: indirect external call! } } while (this._state !== 3 /* AutorunState.upToDate */); } } finally { this._updateCount--; } assertFn(() => this._updateCount >= 0); } handlePossibleChange(observable) { if (this._state === 3 /* AutorunState.upToDate */ && this._isDependency(observable)) { this._state = 1 /* AutorunState.dependenciesMightHaveChanged */; } } handleChange(observable, change) { if (this._isDependency(observable)) { getLogger()?.handleAutorunDependencyChanged(this, observable, change); try { // Warning: external call! const shouldReact = this._changeTracker ? this._changeTracker.handleChange({ changedObservable: observable, change, // eslint-disable-next-line local/code-no-any-casts didChange: (o) => o === observable, }, this._changeSummary) : true; if (shouldReact) { this._state = 2 /* AutorunState.stale */; } } catch (e) { onBugIndicatingError(e); } } } _isDependency(observable) { return this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable); } // IReader implementation _ensureNoRunning() { if (!this._isRunning) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); } } readObservable(observable) { this._ensureNoRunning(); // In case the run action disposes the autorun if (this._disposed) { return observable.get(); // warning: external call! } observable.addObserver(this); // warning: external call! const value = observable.get(); // warning: external call! this._dependencies.add(observable); this._dependenciesToBeRemoved.delete(observable); return value; } get store() { this._ensureNoRunning(); if (this._disposed) { throw new BugIndicatingError('Cannot access store after dispose'); } if (this._store === undefined) { this._store = new DisposableStore(); } return this._store; } debugGetState() { return { isRunning: this._isRunning, updateCount: this._updateCount, dependencies: this._dependencies, state: this._state, stateStr: autorunStateToString(this._state), }; } debugRerun() { if (!this._isRunning) { this._run(); } else { this._state = 2 /* AutorunState.stale */; } } } export { AutorunObserver };