@eclipse-glsp/theia-integration
Version:
Glue code to integrate GLSP clients into Eclipse Theia
201 lines (172 loc) • 7.84 kB
text/typescript
/********************************************************************************
* Copyright (c) 2018-2026 TypeFox and others.
* Modifications: (c) 2019-2024 EclipseSource 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 WITH Classpath-exception-2.0
********************************************************************************/
// based on: https://github.com/eclipse-sprotty/sprotty-theia/blob/v0.12.0/src/theia/diagram-manager.ts
import { codiconCSSString, EditMode, IDiagramOptions } from '@eclipse-glsp/client';
import { URI } from '@theia/core';
import {
ApplicationShell,
FrontendApplicationContribution,
OpenHandler,
OpenWithHandler,
OpenWithService,
WidgetFactory,
WidgetOpenerOptions,
WidgetOpenHandler
} from '@theia/core/lib/browser';
import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
import { EditorManager } from '@theia/editor/lib/browser';
import { DiagramServiceProvider } from '../diagram-service-provider';
import { TheiaOpenerOptionsNavigationService } from '../theia-opener-options-navigation-service';
import { DiagramConfiguration } from './glsp-diagram-configuration';
import { GLSPDiagramContextKeyService } from './glsp-diagram-context-key-service';
import { GLSPDiagramWidget, GLSPDiagramWidgetOptions } from './glsp-diagram-widget';
export function registerDiagramManager(
bind: interfaces.Bind,
diagramManagerServiceId: interfaces.ServiceIdentifier<GLSPDiagramManager>,
bindToSelf = true
): void {
if (bindToSelf) {
bind(diagramManagerServiceId).toSelf().inSingletonScope();
}
bind(FrontendApplicationContribution).toService(diagramManagerServiceId);
bind(OpenHandler).toService(diagramManagerServiceId);
bind(WidgetFactory).toService(diagramManagerServiceId);
}
export abstract class GLSPDiagramManager extends WidgetOpenHandler<GLSPDiagramWidget> implements WidgetFactory, OpenWithHandler {
protected readonly diagramNavigationService: TheiaOpenerOptionsNavigationService;
protected readonly contextKeyService: GLSPDiagramContextKeyService;
protected readonly diagramServiceProvider: DiagramServiceProvider;
protected readonly editorManager: EditorManager;
protected openWithService: OpenWithService;
abstract get fileExtensions(): string[];
abstract get diagramType(): string;
abstract get contributionId(): string;
protected widgetCount = 0;
protected registerOpenWithHandler = true;
protected override init(): void {
super.init();
if (this.registerOpenWithHandler) {
this.openWithService.registerHandler(this);
}
}
override async doOpen(widget: GLSPDiagramWidget, uri: URI, maybeOptions?: WidgetOpenerOptions): Promise<void> {
const options: WidgetOpenerOptions = {
mode: 'activate',
...maybeOptions
};
if (!widget.isAttached) {
this.attachWidget(widget, options);
}
if (options.mode === 'activate') {
await widget.getSvgElement();
await this.shell.activateWidget(widget.id);
} else if (options.mode === 'reveal') {
await this.shell.revealWidget(widget.id);
}
if (this.handleNavigations(widget, options)) {
return;
}
}
protected attachWidget(widget: GLSPDiagramWidget, options?: WidgetOpenerOptions): void {
const currentEditor = this.editorManager.currentEditor;
const widgetOptions: ApplicationShell.WidgetOptions = {
area: 'main',
...(options && options.widgetOptions ? options.widgetOptions : {})
};
if (!!currentEditor && currentEditor.editor.uri.toString(true) === widget.uri.toString(true)) {
widgetOptions.ref = currentEditor;
widgetOptions.mode =
options && options.widgetOptions && options.widgetOptions.mode ? options.widgetOptions.mode : 'open-to-right';
}
this.shell.addWidget(widget, widgetOptions);
}
protected handleNavigations(widget: GLSPDiagramWidget, options?: WidgetOpenerOptions): boolean {
const navigations = this.diagramNavigationService.determineNavigations(widget.uri.toString(true), options);
if (navigations.length > 0) {
widget.actionDispatcher.dispatchOnceModelInitialized(...navigations);
return true;
}
return false;
}
async createWidget(options?: any): Promise<GLSPDiagramWidget> {
if (GLSPDiagramWidgetOptions.is(options)) {
const config = this.getDiagramConfiguration(options);
const diagramOptions = this.createDiagramOptions(options);
const diContainer = config.createContainer(diagramOptions);
const diagramWidgetFactory = this.diagramServiceProvider.getDiagramWidgetFactory(this.diagramType);
const widget = diagramWidgetFactory?.create(options, diContainer);
widget.listenToFocusState(this.shell);
return widget;
}
throw Error('DiagramWidgetFactory needs DiagramWidgetOptions but got ' + JSON.stringify(options));
}
protected createDiagramOptions(options: GLSPDiagramWidgetOptions): IDiagramOptions {
return {
clientId: this.createClientId(),
diagramType: options.diagramType,
sourceUri: options.uri,
editMode: options.editMode,
glspClientProvider: () => this.diagramServiceProvider.getGLSPClientContribution(this.contributionId).glspClient
};
}
protected createClientId(): string {
return this.diagramType + '_' + this.widgetCount++;
}
protected override createWidgetOptions(uri: URI, options?: GLSPWidgetOpenerOptions): GLSPDiagramWidgetOptions {
return {
diagramType: this.diagramType,
kind: 'navigatable',
uri: uri.toString(true),
iconClass: this.iconClass,
label: uri.path.base,
editMode: options && options.editMode ? options.editMode : EditMode.EDITABLE
};
}
protected getDiagramConfiguration(options: GLSPDiagramWidgetOptions): DiagramConfiguration {
return this.diagramServiceProvider.getDiagramConfiguration(options.diagramType);
}
canHandle(uri: URI, _options?: WidgetOpenerOptions | undefined): number {
for (const extension of this.fileExtensions) {
if (uri.path.toString().endsWith(extension)) {
return 1001;
}
}
return 0;
}
override get id(): string {
return deriveDiagramManagerId(this.diagramType);
}
get iconClass(): string {
return codiconCSSString('type-hierarchy-sub');
}
get providerName(): string | undefined {
return undefined;
}
}
export interface GLSPWidgetOpenerOptions extends WidgetOpenerOptions {
editMode?: string;
}
export function deriveDiagramManagerId(diagramType: string): string {
return diagramType + '-diagram-manager';
}