1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | var ShellLayoutRestorer_1;
|
18 | Object.defineProperty(exports, "__esModule", { value: true });
|
19 | exports.ShellLayoutRestorer = exports.RESET_LAYOUT = exports.ApplicationShellLayoutMigration = exports.ApplicationShellLayoutMigrationError = exports.StatefulWidget = void 0;
|
20 | const tslib_1 = require("tslib");
|
21 | const inversify_1 = require("inversify");
|
22 | const widget_manager_1 = require("../widget-manager");
|
23 | const storage_service_1 = require("../storage-service");
|
24 | const logger_1 = require("../../common/logger");
|
25 | const command_1 = require("../../common/command");
|
26 | const theming_1 = require("../theming");
|
27 | const contribution_provider_1 = require("../../common/contribution-provider");
|
28 | const application_shell_1 = require("./application-shell");
|
29 | const common_frontend_contribution_1 = require("../common-frontend-contribution");
|
30 | const window_service_1 = require("../window/window-service");
|
31 | const frontend_application_state_1 = require("../../common/frontend-application-state");
|
32 | const common_1 = require("../../common");
|
33 | var StatefulWidget;
|
34 | (function (StatefulWidget) {
|
35 | function is(arg) {
|
36 | return (0, common_1.isObject)(arg) && (0, common_1.isFunction)(arg.storeState) && (0, common_1.isFunction)(arg.restoreState);
|
37 | }
|
38 | StatefulWidget.is = is;
|
39 | })(StatefulWidget = exports.StatefulWidget || (exports.StatefulWidget = {}));
|
40 | var ApplicationShellLayoutMigrationError;
|
41 | (function (ApplicationShellLayoutMigrationError) {
|
42 | const code = 'ApplicationShellLayoutMigrationError';
|
43 | function create(message) {
|
44 | return Object.assign(new Error(`Could not migrate layout to version ${application_shell_1.applicationShellLayoutVersion}.` + (message ? '\n' + message : '')), { code });
|
45 | }
|
46 | ApplicationShellLayoutMigrationError.create = create;
|
47 | function is(error) {
|
48 | return !!error && 'code' in error && error['code'] === code;
|
49 | }
|
50 | ApplicationShellLayoutMigrationError.is = is;
|
51 | })(ApplicationShellLayoutMigrationError = exports.ApplicationShellLayoutMigrationError || (exports.ApplicationShellLayoutMigrationError = {}));
|
52 | exports.ApplicationShellLayoutMigration = Symbol('ApplicationShellLayoutMigration');
|
53 | exports.RESET_LAYOUT = command_1.Command.toLocalizedCommand({
|
54 | id: 'reset.layout',
|
55 | category: common_frontend_contribution_1.CommonCommands.VIEW_CATEGORY,
|
56 | label: 'Reset Workbench Layout'
|
57 | }, 'theia/core/resetWorkbenchLayout', common_frontend_contribution_1.CommonCommands.VIEW_CATEGORY_KEY);
|
58 | let ShellLayoutRestorer = ShellLayoutRestorer_1 = class ShellLayoutRestorer {
|
59 | constructor(widgetManager, logger, storageService) {
|
60 | this.widgetManager = widgetManager;
|
61 | this.logger = logger;
|
62 | this.storageService = storageService;
|
63 | this.storageKey = 'layout';
|
64 | this.shouldStoreLayout = true;
|
65 | }
|
66 | registerCommands(commands) {
|
67 | commands.registerCommand(exports.RESET_LAYOUT, {
|
68 | execute: async () => this.resetLayout()
|
69 | });
|
70 | }
|
71 | async resetLayout() {
|
72 | if (await this.windowService.isSafeToShutDown(frontend_application_state_1.StopReason.Reload)) {
|
73 | this.logger.info('>>> Resetting layout...');
|
74 | this.shouldStoreLayout = false;
|
75 | this.storageService.setData(this.storageKey, undefined);
|
76 | this.themeService.reset();
|
77 | this.logger.info('<<< The layout has been successfully reset.');
|
78 | this.windowService.reload();
|
79 | }
|
80 | }
|
81 | storeLayout(app) {
|
82 | if (this.shouldStoreLayout) {
|
83 | try {
|
84 | this.logger.info('>>> Storing the layout...');
|
85 | const layoutData = app.shell.getLayoutData();
|
86 | const serializedLayoutData = this.deflate(layoutData);
|
87 | this.storageService.setData(this.storageKey, serializedLayoutData);
|
88 | this.logger.info('<<< The layout has been successfully stored.');
|
89 | }
|
90 | catch (error) {
|
91 | this.storageService.setData(this.storageKey, undefined);
|
92 | this.logger.error('Error during serialization of layout data', error);
|
93 | }
|
94 | }
|
95 | }
|
96 | async restoreLayout(app) {
|
97 | this.logger.info('>>> Restoring the layout state...');
|
98 | const serializedLayoutData = await this.storageService.getData(this.storageKey);
|
99 | if (serializedLayoutData === undefined) {
|
100 | this.logger.info('<<< Nothing to restore.');
|
101 | return false;
|
102 | }
|
103 | const layoutData = await this.inflate(serializedLayoutData);
|
104 | await app.shell.setLayoutData(layoutData);
|
105 | this.logger.info('<<< The layout has been successfully restored.');
|
106 | return true;
|
107 | }
|
108 | isWidgetProperty(propertyName) {
|
109 | return propertyName === 'widget';
|
110 | }
|
111 | isWidgetsProperty(propertyName) {
|
112 | return propertyName === 'widgets';
|
113 | }
|
114 | |
115 |
|
116 |
|
117 | deflate(data) {
|
118 | return JSON.stringify(data, (property, value) => {
|
119 | if (this.isWidgetProperty(property)) {
|
120 | const description = this.convertToDescription(value);
|
121 | return description;
|
122 | }
|
123 | else if (this.isWidgetsProperty(property)) {
|
124 | const descriptions = [];
|
125 | for (const widget of value) {
|
126 | const description = this.convertToDescription(widget);
|
127 | if (description) {
|
128 | descriptions.push(description);
|
129 | }
|
130 | }
|
131 | return descriptions;
|
132 | }
|
133 | return value;
|
134 | });
|
135 | }
|
136 | convertToDescription(widget) {
|
137 | const desc = this.widgetManager.getDescription(widget);
|
138 | if (desc) {
|
139 | if (StatefulWidget.is(widget)) {
|
140 | const innerState = widget.storeState();
|
141 | return innerState ? {
|
142 | constructionOptions: desc,
|
143 | innerWidgetState: this.deflate(innerState)
|
144 | } : undefined;
|
145 | }
|
146 | else {
|
147 | return {
|
148 | constructionOptions: desc,
|
149 | innerWidgetState: undefined
|
150 | };
|
151 | }
|
152 | }
|
153 | }
|
154 | |
155 |
|
156 |
|
157 | async inflate(layoutData) {
|
158 | const parseContext = new ShellLayoutRestorer_1.ParseContext();
|
159 | const layout = this.parse(layoutData, parseContext);
|
160 | const layoutVersion = Number(layout.version);
|
161 | if (typeof layoutVersion !== 'number' || Number.isNaN(layoutVersion)) {
|
162 | throw new Error('could not resolve a layout version');
|
163 | }
|
164 | if (layoutVersion !== application_shell_1.applicationShellLayoutVersion) {
|
165 | if (layoutVersion < application_shell_1.applicationShellLayoutVersion) {
|
166 | console.warn(`Layout version ${layoutVersion} is behind current layout version ${application_shell_1.applicationShellLayoutVersion}, trying to migrate...`);
|
167 | }
|
168 | else {
|
169 | console.warn(`Layout version ${layoutVersion} is ahead current layout version ${application_shell_1.applicationShellLayoutVersion}, trying to load anyway...`);
|
170 | }
|
171 | console.info(`Please use '${exports.RESET_LAYOUT.label}' command if the layout looks bogus.`);
|
172 | }
|
173 | const migrations = this.migrations.getContributions()
|
174 | .filter(m => m.layoutVersion > layoutVersion && m.layoutVersion <= application_shell_1.applicationShellLayoutVersion)
|
175 | .sort((m, m2) => m.layoutVersion - m2.layoutVersion);
|
176 | if (migrations.length) {
|
177 | console.info(`Found ${migrations.length} migrations from layout version ${layoutVersion} to version ${application_shell_1.applicationShellLayoutVersion}, migrating...`);
|
178 | }
|
179 | const context = { layout, layoutVersion, migrations };
|
180 | await this.fireWillInflateLayout(context);
|
181 | await parseContext.inflate(context);
|
182 | return layout;
|
183 | }
|
184 | async fireWillInflateLayout(context) {
|
185 | for (const migration of context.migrations) {
|
186 | if (migration.onWillInflateLayout) {
|
187 |
|
188 | await migration.onWillInflateLayout(context);
|
189 | }
|
190 | }
|
191 | }
|
192 | parse(layoutData, parseContext) {
|
193 | return JSON.parse(layoutData, (property, value) => {
|
194 | if (this.isWidgetsProperty(property)) {
|
195 | const widgets = parseContext.filteredArray();
|
196 | const descs = value;
|
197 | for (let i = 0; i < descs.length; i++) {
|
198 | parseContext.push(async (context) => {
|
199 | widgets[i] = await this.convertToWidget(descs[i], context);
|
200 | });
|
201 | }
|
202 | return widgets;
|
203 | }
|
204 | else if ((0, common_1.isObject)(value) && !Array.isArray(value)) {
|
205 | const copy = {};
|
206 | for (const p in value) {
|
207 | if (this.isWidgetProperty(p)) {
|
208 | parseContext.push(async (context) => {
|
209 | copy[p] = await this.convertToWidget(value[p], context);
|
210 | });
|
211 | }
|
212 | else {
|
213 | copy[p] = value[p];
|
214 | }
|
215 | }
|
216 | return copy;
|
217 | }
|
218 | return value;
|
219 | });
|
220 | }
|
221 | async fireWillInflateWidget(desc, context) {
|
222 | for (const migration of context.migrations) {
|
223 | if (migration.onWillInflateWidget) {
|
224 |
|
225 | const migrated = await migration.onWillInflateWidget(desc, context);
|
226 | if (migrated) {
|
227 | if ((0, common_1.isObject)(migrated.innerWidgetState)) {
|
228 |
|
229 | migrated.innerWidgetState = JSON.stringify(migrated.innerWidgetState);
|
230 | }
|
231 | desc = migrated;
|
232 | }
|
233 | }
|
234 | }
|
235 | return desc;
|
236 | }
|
237 | async convertToWidget(desc, context) {
|
238 | if (!desc.constructionOptions) {
|
239 | return undefined;
|
240 | }
|
241 | try {
|
242 | desc = await this.fireWillInflateWidget(desc, context);
|
243 | const widget = await this.widgetManager.getOrCreateWidget(desc.constructionOptions.factoryId, desc.constructionOptions.options);
|
244 | if (StatefulWidget.is(widget) && desc.innerWidgetState !== undefined) {
|
245 | try {
|
246 | let oldState;
|
247 | if (typeof desc.innerWidgetState === 'string') {
|
248 | const parseContext = new ShellLayoutRestorer_1.ParseContext();
|
249 | oldState = this.parse(desc.innerWidgetState, parseContext);
|
250 | await parseContext.inflate({ ...context, parent: widget });
|
251 | }
|
252 | else {
|
253 | oldState = desc.innerWidgetState;
|
254 | }
|
255 | widget.restoreState(oldState);
|
256 | }
|
257 | catch (e) {
|
258 | if (ApplicationShellLayoutMigrationError.is(e)) {
|
259 | throw e;
|
260 | }
|
261 | this.logger.warn(`Couldn't restore widget state for ${widget.id}. Error: ${e} `);
|
262 | }
|
263 | }
|
264 | if (widget.isDisposed) {
|
265 | return undefined;
|
266 | }
|
267 | return widget;
|
268 | }
|
269 | catch (e) {
|
270 | if (ApplicationShellLayoutMigrationError.is(e)) {
|
271 | throw e;
|
272 | }
|
273 | this.logger.warn(`Couldn't restore widget for ${desc.constructionOptions.factoryId}. Error: ${e} `);
|
274 | return undefined;
|
275 | }
|
276 | }
|
277 | };
|
278 | (0, tslib_1.__decorate)([
|
279 | (0, inversify_1.inject)(contribution_provider_1.ContributionProvider),
|
280 | (0, inversify_1.named)(exports.ApplicationShellLayoutMigration),
|
281 | (0, tslib_1.__metadata)("design:type", Object)
|
282 | ], ShellLayoutRestorer.prototype, "migrations", void 0);
|
283 | (0, tslib_1.__decorate)([
|
284 | (0, inversify_1.inject)(window_service_1.WindowService),
|
285 | (0, tslib_1.__metadata)("design:type", Object)
|
286 | ], ShellLayoutRestorer.prototype, "windowService", void 0);
|
287 | (0, tslib_1.__decorate)([
|
288 | (0, inversify_1.inject)(theming_1.ThemeService),
|
289 | (0, tslib_1.__metadata)("design:type", theming_1.ThemeService)
|
290 | ], ShellLayoutRestorer.prototype, "themeService", void 0);
|
291 | ShellLayoutRestorer = ShellLayoutRestorer_1 = (0, tslib_1.__decorate)([
|
292 | (0, inversify_1.injectable)(),
|
293 | (0, tslib_1.__param)(0, (0, inversify_1.inject)(widget_manager_1.WidgetManager)),
|
294 | (0, tslib_1.__param)(1, (0, inversify_1.inject)(logger_1.ILogger)),
|
295 | (0, tslib_1.__param)(2, (0, inversify_1.inject)(storage_service_1.StorageService)),
|
296 | (0, tslib_1.__metadata)("design:paramtypes", [widget_manager_1.WidgetManager, Object, Object])
|
297 | ], ShellLayoutRestorer);
|
298 | exports.ShellLayoutRestorer = ShellLayoutRestorer;
|
299 | (function (ShellLayoutRestorer) {
|
300 | class ParseContext {
|
301 | constructor() {
|
302 | this.toInflate = [];
|
303 | this.toFilter = [];
|
304 | }
|
305 | |
306 |
|
307 |
|
308 |
|
309 | filteredArray() {
|
310 | const array = [];
|
311 | this.toFilter.push(array);
|
312 | return array;
|
313 | }
|
314 | push(toInflate) {
|
315 | this.toInflate.push(toInflate);
|
316 | }
|
317 | async inflate(context) {
|
318 | const pending = [];
|
319 | while (this.toInflate.length) {
|
320 | pending.push(this.toInflate.pop()(context));
|
321 | }
|
322 | await Promise.all(pending);
|
323 | if (this.toFilter.length) {
|
324 | this.toFilter.forEach(array => {
|
325 | for (let i = 0; i < array.length; i++) {
|
326 | if (array[i] === undefined) {
|
327 | array.splice(i--, 1);
|
328 | }
|
329 | }
|
330 | });
|
331 | }
|
332 | }
|
333 | }
|
334 | ShellLayoutRestorer.ParseContext = ParseContext;
|
335 | })(ShellLayoutRestorer = exports.ShellLayoutRestorer || (exports.ShellLayoutRestorer = {}));
|
336 | exports.ShellLayoutRestorer = ShellLayoutRestorer;
|
337 |
|
\ | No newline at end of file |