@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
230 lines • 9.37 kB
JavaScript
"use strict";
// *****************************************************************************
// Copyright (C) 2017 TypeFox 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
// *****************************************************************************
Object.defineProperty(exports, "__esModule", { value: true });
exports.WidgetManager = exports.WidgetFactory = void 0;
const tslib_1 = require("tslib");
const inversify_1 = require("inversify");
const widgets_1 = require("@lumino/widgets");
const common_1 = require("../common");
const stableJsonStringify = require("fast-json-stable-stringify");
/* eslint-disable @typescript-eslint/no-explicit-any */
exports.WidgetFactory = Symbol('WidgetFactory');
/**
* The {@link WidgetManager} is the common component responsible for creating and managing widgets. Additional widget factories
* can be registered by using the {@link WidgetFactory} contribution point. To identify a widget, created by a factory, the factory id and
* the creation options are used. This key is commonly referred to as `description` of the widget.
*/
let WidgetManager = class WidgetManager {
constructor() {
this.widgets = new Map();
this.pendingWidgetPromises = new Map();
this.onWillCreateWidgetEmitter = new common_1.Emitter();
/**
* An event can be used to participate in the widget creation.
* Listeners may not dispose the given widget.
*/
this.onWillCreateWidget = this.onWillCreateWidgetEmitter.event;
this.onDidCreateWidgetEmitter = new common_1.Emitter();
this.onDidCreateWidget = this.onDidCreateWidgetEmitter.event;
}
/**
* Get the list of widgets created by the given widget factory.
* @param factoryId the widget factory id.
*
* @returns the list of widgets created by the factory with the given id.
*/
getWidgets(factoryId) {
const result = [];
for (const [key, widget] of this.widgets.entries()) {
if (this.fromKey(key).factoryId === factoryId) {
result.push(widget);
}
}
return result;
}
/**
* Try to get the existing widget for the given description.
* @param factoryId The widget factory id.
* @param options The widget factory specific information.
*
* @returns the widget if available, else `undefined`.
*
* The widget is 'available' if it has been created with the same {@link factoryId} and {@link options} by the {@link WidgetManager}.
* If the widget's creation is asynchronous, it is only available when the associated `Promise` is resolved.
*/
tryGetWidget(factoryId, options) {
const key = this.toKey({ factoryId, options });
const existing = this.widgets.get(key);
if (existing instanceof widgets_1.Widget) {
return existing;
}
return undefined;
}
/**
* Try to get the existing widget for the given description.
* @param factoryId The widget factory id.
* @param options The widget factory specific information.
*
* @returns A promise that resolves to the widget, if any exists. The promise may be pending, so be cautious when assuming that it will not reject.
*/
tryGetPendingWidget(factoryId, options) {
const key = this.toKey({ factoryId, options });
return this.doGetWidget(key);
}
/**
* Get the widget for the given description.
* @param factoryId The widget factory id.
* @param options The widget factory specific information.
*
* @returns a promise resolving to the widget if available, else `undefined`.
*/
async getWidget(factoryId, options) {
const key = this.toKey({ factoryId, options });
const pendingWidget = this.doGetWidget(key);
const widget = pendingWidget && await pendingWidget;
return widget;
}
/**
* Finds a widget that matches the given test predicate.
* @param factoryId The widget factory id.
* @param predicate The test predicate.
*
* @returns a promise resolving to the widget if available, else `undefined`.
*/
async findWidget(factoryId, predicate) {
for (const [key, widget] of this.widgets.entries()) {
if (this.testPredicate(key, factoryId, predicate)) {
return widget;
}
}
for (const [key, widgetPromise] of this.pendingWidgetPromises.entries()) {
if (this.testPredicate(key, factoryId, predicate)) {
return widgetPromise;
}
}
}
testPredicate(key, factoryId, predicate) {
const constructionOptions = this.fromKey(key);
return constructionOptions.factoryId === factoryId && predicate(constructionOptions.options);
}
doGetWidget(key) {
var _a;
const pendingWidget = (_a = this.widgets.get(key)) !== null && _a !== void 0 ? _a : this.pendingWidgetPromises.get(key);
if (pendingWidget) {
return pendingWidget;
}
return undefined;
}
/**
* Creates a new widget or returns the existing widget for the given description.
* @param factoryId the widget factory id.
* @param options the widget factory specific information.
*
* @returns a promise resolving to the widget.
*/
async getOrCreateWidget(factoryId, options) {
const key = this.toKey({ factoryId, options });
const existingWidget = this.doGetWidget(key);
if (existingWidget) {
return existingWidget;
}
const factory = this.factories.get(factoryId);
if (!factory) {
throw Error("No widget factory '" + factoryId + "' has been registered.");
}
const widgetPromise = this.doCreateWidget(factory, options).then(widget => {
this.widgets.set(key, widget);
widget.disposed.connect(() => this.widgets.delete(key));
this.onDidCreateWidgetEmitter.fire({ factoryId, widget });
return widget;
}).finally(() => this.pendingWidgetPromises.delete(key));
this.pendingWidgetPromises.set(key, widgetPromise);
return widgetPromise;
}
async doCreateWidget(factory, options) {
const widget = await factory.createWidget(options);
// Note: the widget creation process also includes the 'onWillCreateWidget' part, which can potentially fail
try {
await common_1.WaitUntilEvent.fire(this.onWillCreateWidgetEmitter, { factoryId: factory.id, widget });
}
catch (e) {
widget.dispose();
throw e;
}
return widget;
}
/**
* Get the widget construction options.
* @param widget the widget.
*
* @returns the widget construction options if the widget was created through the manager, else `undefined`.
*/
getDescription(widget) {
for (const [key, aWidget] of this.widgets.entries()) {
if (aWidget === widget) {
return this.fromKey(key);
}
}
return undefined;
}
/**
* Convert the widget construction options to string.
* @param options the widget construction options.
*
* @returns the widget construction options represented as a string.
*/
toKey(options) {
return stableJsonStringify(options);
}
/**
* Convert the key into the widget construction options object.
* @param key the key.
*
* @returns the widget construction options object.
*/
fromKey(key) {
return JSON.parse(key);
}
get factories() {
if (!this._cachedFactories) {
this._cachedFactories = new Map();
for (const factory of this.factoryProvider.getContributions()) {
if (factory.id) {
this._cachedFactories.set(factory.id, factory);
}
else {
this.logger.error('Invalid ID for factory: ' + factory + ". ID was: '" + factory.id + "'.");
}
}
}
return this._cachedFactories;
}
};
exports.WidgetManager = WidgetManager;
tslib_1.__decorate([
(0, inversify_1.inject)(common_1.ContributionProvider),
(0, inversify_1.named)(exports.WidgetFactory),
tslib_1.__metadata("design:type", Object)
], WidgetManager.prototype, "factoryProvider", void 0);
tslib_1.__decorate([
(0, inversify_1.inject)(common_1.ILogger),
tslib_1.__metadata("design:type", Object)
], WidgetManager.prototype, "logger", void 0);
exports.WidgetManager = WidgetManager = tslib_1.__decorate([
(0, inversify_1.injectable)()
], WidgetManager);
//# sourceMappingURL=widget-manager.js.map