UNPKG

@theia/core

Version:

Theia is a cloud & desktop IDE framework implemented in TypeScript.

194 lines • 9.35 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2025 1C-Soft LLC and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // copied and modified from https://github.com/microsoft/vscode/blob/1.96.3/src/vs/base/common/observableInternal/autorun.ts Object.defineProperty(exports, "__esModule", { value: true }); exports.Autorun = void 0; const observable_base_1 = require("./observable-base"); class Autorun { constructor(doRun, options) { var _a; this.doRun = doRun; this.state = 1 /* Autorun.State.Stale */; this.updateCount = 0; this.disposed = false; this.isRunning = false; this.dependencies = new Set(); this.dependencyObserver = this.createDependencyObserver(); this.createChangeSummary = options === null || options === void 0 ? void 0 : options.createChangeSummary; this.willHandleChange = options === null || options === void 0 ? void 0 : options.willHandleChange; this.changeSummary = (_a = this.createChangeSummary) === null || _a === void 0 ? void 0 : _a.call(this); try { this.run(true); } catch (e) { this.dispose(); throw e; } } dispose() { if (this.disposed) { return; } this.disposed = true; for (const dependency of this.dependencies) { dependency.removeObserver(this.dependencyObserver); } this.dependencies.clear(); } run(isFirstRun = false) { var _a; if (this.disposed) { return; } this.dependenciesToBeRemoved = this.dependencies; this.dependencies = new Set(); this.state = 2 /* Autorun.State.UpToDate */; try { const { changeSummary } = this; this.changeSummary = (_a = this.createChangeSummary) === null || _a === void 0 ? void 0 : _a.call(this); this.isRunning = true; observable_base_1.Observable.Accessor.runWithAccessor(() => this.doRun({ autorun: this, isFirstRun, changeSummary }), dependency => this.watchDependency(dependency)); } finally { this.isRunning = false; // We don't want our watched dependencies to think that they are no longer observed, even temporarily. // Thus, we only unsubscribe from dependencies that are definitely not watched anymore. for (const dependency of this.dependenciesToBeRemoved) { dependency.removeObserver(this.dependencyObserver); } this.dependenciesToBeRemoved = undefined; } } watchDependency(dependency) { var _a; if (!this.isRunning) { throw new Error('The accessor may only be called while the autorun is running'); } // In case the run action disposed the autorun. if (this.disposed) { return dependency.getUntracked(); } // Subscribe before getting the value to enable caching. dependency.addObserver(this.dependencyObserver); // This might call handleChange indirectly, which could invalidate us. const value = dependency.getUntracked(); // Which is why we only add the observable to the dependencies now. this.dependencies.add(dependency); (_a = this.dependenciesToBeRemoved) === null || _a === void 0 ? void 0 : _a.delete(dependency); return value; } createDependencyObserver() { return { beginUpdate: () => { if (this.state === 2 /* Autorun.State.UpToDate */) { this.state = 0 /* Autorun.State.DependenciesMightHaveChanged */; } this.updateCount++; }, endUpdate: () => { this.updateCount--; if (this.updateCount === 0) { do { if (this.state === 0 /* Autorun.State.DependenciesMightHaveChanged */) { this.state = 2 /* Autorun.State.UpToDate */; for (const dependency of this.dependencies) { dependency.update(); // might call handleChange indirectly, which could make us stale if (this.state === 1 /* Autorun.State.Stale */) { // The other dependencies will refresh on demand break; } } } if (this.state !== 2 /* Autorun.State.UpToDate */) { try { this.run(); } catch (e) { console.error(e); } } // In case the run action changed one of our dependencies, we need to run again. } while (this.state !== 2 /* Autorun.State.UpToDate */); } if (this.updateCount < 0) { throw new Error('Unexpected update count: ' + this.updateCount); } }, handlePossibleChange: (observable) => { var _a; if (this.state === 2 /* Autorun.State.UpToDate */ && this.dependencies.has(observable) && !((_a = this.dependenciesToBeRemoved) === null || _a === void 0 ? void 0 : _a.has(observable))) { this.state = 0 /* Autorun.State.DependenciesMightHaveChanged */; } }, handleChange: (observable, change) => { var _a; if (this.dependencies.has(observable) && !((_a = this.dependenciesToBeRemoved) === null || _a === void 0 ? void 0 : _a.has(observable))) { let shouldReact = true; if (this.willHandleChange) { try { shouldReact = this.willHandleChange({ observable, change, isChangeOf: (o) => o === observable }, this.changeSummary); } catch (e) { console.error(e); } } if (shouldReact) { this.state = 1 /* Autorun.State.Stale */; } } } }; } } exports.Autorun = Autorun; (function (Autorun) { /** * Runs the given {@link run} function immediately, and whenever an update scope ends * and an observable tracked as a dependency of the autorun has changed. * * Note that the run function of the autorun is called within an invocation context where * the {@link Observable.Accessor.getCurrent current accessor} is set to track the autorun * dependencies, so that any observables accessed with `get()` will automatically be tracked. * Occasionally, it might be useful to disable such automatic tracking and track the dependencies * manually with `get(accessor)`. This can be done using the {@link Observable.noAutoTracking} function, * e.g. * ```ts * this.toDispose.push(Autorun.create(() => Observable.noAutoTracking(accessor => { * const value1 = this.observable1.get(accessor); // the autorun will depend on this observable... * const value2 = this.observable2.get(); // ...but not on this observable * }))); * ``` * In particular, this pattern might be useful when copying existing autorun code from VS Code, * where observables can only be tracked manually with `read(reader)`, which corresponds to * `get(accessor)` in Theia; calls to `get()` never cause an observable to be tracked. This directly * corresponds to disabling automatic tracking in Theia with {@link Observable.noAutoTracking}. */ function create(run, options) { return new Autorun(run, options); } Autorun.create = create; ; })(Autorun || (exports.Autorun = Autorun = {})); //# sourceMappingURL=autorun.js.map