127 lines
5.4 KiB
TypeScript
127 lines
5.4 KiB
TypeScript
// *****************************************************************************
|
|
// Copyright (C) 2021 SAP SE or an SAP affiliate company 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
|
|
// *****************************************************************************
|
|
|
|
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
|
|
import { CustomEditor, DeployedPlugin } from '../../../common';
|
|
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
|
|
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
import { CustomEditorOpener } from './custom-editor-opener';
|
|
import { Emitter, PreferenceService } from '@theia/core';
|
|
import { ApplicationShell, DefaultOpenerService, OpenWithService, WidgetManager } from '@theia/core/lib/browser';
|
|
import { CustomEditorWidget } from './custom-editor-widget';
|
|
|
|
@injectable()
|
|
export class PluginCustomEditorRegistry {
|
|
private readonly editors = new Map<string, CustomEditor>();
|
|
private readonly pendingEditors = new Map<CustomEditorWidget, { deferred: Deferred<void>, disposable: Disposable }>();
|
|
private readonly resolvers = new Map<string, (widget: CustomEditorWidget) => Promise<void>>();
|
|
|
|
private readonly onWillOpenCustomEditorEmitter = new Emitter<string>();
|
|
readonly onWillOpenCustomEditor = this.onWillOpenCustomEditorEmitter.event;
|
|
|
|
@inject(DefaultOpenerService)
|
|
protected readonly defaultOpenerService: DefaultOpenerService;
|
|
|
|
@inject(WidgetManager)
|
|
protected readonly widgetManager: WidgetManager;
|
|
|
|
@inject(ApplicationShell)
|
|
protected readonly shell: ApplicationShell;
|
|
|
|
@inject(OpenWithService)
|
|
protected readonly openWithService: OpenWithService;
|
|
|
|
@inject(PreferenceService)
|
|
protected readonly preferenceService: PreferenceService;
|
|
|
|
@postConstruct()
|
|
protected init(): void {
|
|
this.widgetManager.onDidCreateWidget(({ factoryId, widget }) => {
|
|
if (factoryId === CustomEditorWidget.FACTORY_ID && widget instanceof CustomEditorWidget) {
|
|
const restoreState = widget.restoreState.bind(widget);
|
|
|
|
widget.restoreState = state => {
|
|
if (state.viewType && state.strResource) {
|
|
restoreState(state);
|
|
this.resolveWidget(widget);
|
|
} else {
|
|
widget.dispose();
|
|
}
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
registerCustomEditor(editor: CustomEditor, plugin: DeployedPlugin): Disposable {
|
|
if (this.editors.has(editor.viewType)) {
|
|
console.warn('editor with such id already registered: ', JSON.stringify(editor));
|
|
return Disposable.NULL;
|
|
}
|
|
this.editors.set(editor.viewType, editor);
|
|
|
|
const toDispose = new DisposableCollection();
|
|
toDispose.push(Disposable.create(() => this.editors.delete(editor.viewType)));
|
|
|
|
const editorOpenHandler = new CustomEditorOpener(
|
|
editor,
|
|
this.shell,
|
|
this.widgetManager,
|
|
this,
|
|
this.preferenceService
|
|
);
|
|
toDispose.push(this.defaultOpenerService.addHandler(editorOpenHandler));
|
|
toDispose.push(
|
|
this.openWithService.registerHandler({
|
|
id: editor.viewType,
|
|
label: editorOpenHandler.label,
|
|
providerName: plugin.metadata.model.displayName,
|
|
canHandle: uri => editorOpenHandler.canOpenWith(uri),
|
|
open: uri => editorOpenHandler.open(uri)
|
|
})
|
|
);
|
|
return toDispose;
|
|
}
|
|
|
|
async resolveWidget(widget: CustomEditorWidget): Promise<void> {
|
|
const resolver = this.resolvers.get(widget.viewType);
|
|
if (resolver) {
|
|
await resolver(widget);
|
|
} else {
|
|
const deferred = new Deferred<void>();
|
|
const disposable = widget.onDidDispose(() => this.pendingEditors.delete(widget));
|
|
this.pendingEditors.set(widget, { deferred, disposable });
|
|
this.onWillOpenCustomEditorEmitter.fire(widget.viewType);
|
|
return deferred.promise;
|
|
}
|
|
};
|
|
|
|
registerResolver(viewType: string, resolver: (widget: CustomEditorWidget) => Promise<void>): Disposable {
|
|
if (this.resolvers.has(viewType)) {
|
|
throw new Error(`Resolver for ${viewType} already registered`);
|
|
}
|
|
|
|
for (const [editorWidget, { deferred, disposable }] of this.pendingEditors.entries()) {
|
|
if (editorWidget.viewType === viewType) {
|
|
resolver(editorWidget).then(() => deferred.resolve(), err => deferred.reject(err)).finally(() => disposable.dispose());
|
|
this.pendingEditors.delete(editorWidget);
|
|
}
|
|
}
|
|
|
|
this.resolvers.set(viewType, resolver);
|
|
return Disposable.create(() => this.resolvers.delete(viewType));
|
|
}
|
|
}
|