UNPKG

8.78 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2017 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
18 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
19 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
20 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
21 return c > 3 && r && Object.defineProperty(target, key, r), r;
22};
23var __metadata = (this && this.__metadata) || function (k, v) {
24 if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
25};
26Object.defineProperty(exports, "__esModule", { value: true });
27exports.WidgetManager = exports.WidgetFactory = void 0;
28const inversify_1 = require("inversify");
29const widgets_1 = require("@phosphor/widgets");
30const common_1 = require("../common");
31const stableJsonStringify = require("fast-json-stable-stringify");
32/* eslint-disable @typescript-eslint/no-explicit-any */
33exports.WidgetFactory = Symbol('WidgetFactory');
34/**
35 * The {@link WidgetManager} is the common component responsible for creating and managing widgets. Additional widget factories
36 * can be registered by using the {@link WidgetFactory} contribution point. To identify a widget, created by a factory, the factory id and
37 * the creation options are used. This key is commonly referred to as `description` of the widget.
38 */
39let WidgetManager = class WidgetManager {
40 constructor() {
41 this.widgets = new Map();
42 this.pendingWidgetPromises = new Map();
43 this.onWillCreateWidgetEmitter = new common_1.Emitter();
44 /**
45 * An event can be used to participate in the widget creation.
46 * Listeners may not dispose the given widget.
47 */
48 this.onWillCreateWidget = this.onWillCreateWidgetEmitter.event;
49 this.onDidCreateWidgetEmitter = new common_1.Emitter();
50 this.onDidCreateWidget = this.onDidCreateWidgetEmitter.event;
51 }
52 /**
53 * Get the list of widgets created by the given widget factory.
54 * @param factoryId the widget factory id.
55 *
56 * @returns the list of widgets created by the factory with the given id.
57 */
58 getWidgets(factoryId) {
59 const result = [];
60 for (const [key, widget] of this.widgets.entries()) {
61 if (this.fromKey(key).factoryId === factoryId) {
62 result.push(widget);
63 }
64 }
65 return result;
66 }
67 /**
68 * Try to get the existing widget for the given description.
69 * @param factoryId The widget factory id.
70 * @param options The widget factory specific information.
71 *
72 * @returns the widget if available, else `undefined`.
73 *
74 * The widget is 'available' if it has been created with the same {@link factoryId} and {@link options} by the {@link WidgetManager}.
75 * If the widget's creation is asynchronous, it is only available when the associated `Promise` is resolved.
76 */
77 tryGetWidget(factoryId, options) {
78 const key = this.toKey({ factoryId, options });
79 const existing = this.widgets.get(key);
80 if (existing instanceof widgets_1.Widget) {
81 return existing;
82 }
83 return undefined;
84 }
85 /**
86 * Try to get the existing widget for the given description.
87 * @param factoryId The widget factory id.
88 * @param options The widget factory specific information.
89 *
90 * @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.
91 */
92 tryGetPendingWidget(factoryId, options) {
93 const key = this.toKey({ factoryId, options });
94 return this.doGetWidget(key);
95 }
96 /**
97 * Get the widget for the given description.
98 * @param factoryId The widget factory id.
99 * @param options The widget factory specific information.
100 *
101 * @returns a promise resolving to the widget if available, else `undefined`.
102 */
103 async getWidget(factoryId, options) {
104 const key = this.toKey({ factoryId, options });
105 const pendingWidget = this.doGetWidget(key);
106 const widget = pendingWidget && await pendingWidget;
107 return widget;
108 }
109 doGetWidget(key) {
110 var _a;
111 const pendingWidget = (_a = this.widgets.get(key)) !== null && _a !== void 0 ? _a : this.pendingWidgetPromises.get(key);
112 if (pendingWidget) {
113 return pendingWidget;
114 }
115 return undefined;
116 }
117 /**
118 * Creates a new widget or returns the existing widget for the given description.
119 * @param factoryId the widget factory id.
120 * @param options the widget factory specific information.
121 *
122 * @returns a promise resolving to the widget.
123 */
124 async getOrCreateWidget(factoryId, options) {
125 const key = this.toKey({ factoryId, options });
126 const existingWidget = this.doGetWidget(key);
127 if (existingWidget) {
128 return existingWidget;
129 }
130 const factory = this.factories.get(factoryId);
131 if (!factory) {
132 throw Error("No widget factory '" + factoryId + "' has been registered.");
133 }
134 try {
135 const widgetPromise = factory.createWidget(options);
136 this.pendingWidgetPromises.set(key, widgetPromise);
137 const widget = await widgetPromise;
138 await common_1.WaitUntilEvent.fire(this.onWillCreateWidgetEmitter, { factoryId, widget });
139 this.widgets.set(key, widget);
140 widget.disposed.connect(() => this.widgets.delete(key));
141 this.onDidCreateWidgetEmitter.fire({ factoryId, widget });
142 return widget;
143 }
144 finally {
145 this.pendingWidgetPromises.delete(key);
146 }
147 }
148 /**
149 * Get the widget construction options.
150 * @param widget the widget.
151 *
152 * @returns the widget construction options if the widget was created through the manager, else `undefined`.
153 */
154 getDescription(widget) {
155 for (const [key, aWidget] of this.widgets.entries()) {
156 if (aWidget === widget) {
157 return this.fromKey(key);
158 }
159 }
160 return undefined;
161 }
162 /**
163 * Convert the widget construction options to string.
164 * @param options the widget construction options.
165 *
166 * @returns the widget construction options represented as a string.
167 */
168 toKey(options) {
169 return stableJsonStringify(options);
170 }
171 /**
172 * Convert the key into the widget construction options object.
173 * @param key the key.
174 *
175 * @returns the widget construction options object.
176 */
177 fromKey(key) {
178 return JSON.parse(key);
179 }
180 get factories() {
181 if (!this._cachedFactories) {
182 this._cachedFactories = new Map();
183 for (const factory of this.factoryProvider.getContributions()) {
184 if (factory.id) {
185 this._cachedFactories.set(factory.id, factory);
186 }
187 else {
188 this.logger.error('Invalid ID for factory: ' + factory + ". ID was: '" + factory.id + "'.");
189 }
190 }
191 }
192 return this._cachedFactories;
193 }
194};
195__decorate([
196 (0, inversify_1.inject)(common_1.ContributionProvider),
197 (0, inversify_1.named)(exports.WidgetFactory),
198 __metadata("design:type", Object)
199], WidgetManager.prototype, "factoryProvider", void 0);
200__decorate([
201 (0, inversify_1.inject)(common_1.ILogger),
202 __metadata("design:type", Object)
203], WidgetManager.prototype, "logger", void 0);
204WidgetManager = __decorate([
205 (0, inversify_1.injectable)()
206], WidgetManager);
207exports.WidgetManager = WidgetManager;
208//# sourceMappingURL=widget-manager.js.map
\No newline at end of file