Skip to content

Commit

Permalink
GLSP-960: Improve Diagram container configuration (#174)
Browse files Browse the repository at this point in the history
Update Theia integration code to conform to latest client changes

Requires eclipse-glsp/glsp-client#277
  • Loading branch information
tortmayr authored Aug 26, 2023
1 parent 591932b commit b79696c
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 391 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import { WorkflowLanguage } from '../../common/workflow-language';
export class WorkflowDiagramConfiguration extends GLSPDiagramConfiguration {
diagramType: string = WorkflowLanguage.diagramType;

configureContainer(container: Container, widgetId: string, ...containerConfiguration: ContainerConfiguration): Container {
initializeWorkflowDiagramContainer(container, widgetId, ...containerConfiguration);
configureContainer(container: Container, ...containerConfiguration: ContainerConfiguration): Container {
initializeWorkflowDiagramContainer(container, ...containerConfiguration);
return container;
}
}
3 changes: 2 additions & 1 deletion examples/workflow-theia/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"rootDir": "src",
"outDir": "lib",
"baseUrl": ".",
"resolveJsonModule": true
"resolveJsonModule": true,
"module": "Node16"
},
"include": ["src"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class TheiaExportSvgActionHandler implements IActionHandler {
}

async export(action: ExportSvgAction): Promise<void> {
const uri = await this.editorContextService.getSourceUri();
const uri = this.editorContextService.sourceUri;
const folder = await this.fileService.resolve(new URI(uri));
let file = await this.fileDialogService.showSaveDialog({ title: 'Export Diagram', filters: { 'Images (*.svg)': ['svg'] } }, folder);
if (file) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { ContainerConfiguration, InstanceRegistry } from '@eclipse-glsp/client';
import { Container, inject, injectable, multiInject, optional } from '@theia/core/shared/inversify';
import { ContainerConfiguration, IDiagramOptions, InstanceRegistry, createDiagramOptionsModule } from '@eclipse-glsp/client';
import { Container, ContainerModule, inject, injectable, multiInject, optional } from '@theia/core/shared/inversify';
import { TheiaContextMenuService } from '../theia-glsp-context-menu-service';
import { THEIA_DEFAULT_MODULES } from './features/default-modules';
import { TheiaContextMenuServiceFactory, connectTheiaContextMenuService } from './theia-context-menu-service';
Expand All @@ -35,11 +35,11 @@ export const DiagramConfiguration = Symbol('DiagramConfiguration');
export interface DiagramConfiguration {
/**
* Creates a new diagram container for a widget with the given id and container configuration
* @param widgetId The id of the corresponding diagram widget
* @param options The diagram specific configuration options.
* @param containerConfiguration Additional container configuration.
* Typically modules that are scoped to the Theia application context and should be loaded on top of the generic diagram modules.
*/
createContainer(widgetId: string, ...containerConfiguration: ContainerConfiguration): Container;
createContainer(options: IDiagramOptions, ...containerConfiguration: ContainerConfiguration): Container;
/** The id of the corresponding `DiagramWidget` */
readonly diagramType: string;
}
Expand Down Expand Up @@ -74,13 +74,18 @@ export abstract class GLSPDiagramConfiguration implements DiagramConfiguration {

abstract readonly diagramType: string;

createContainer(widgetId: string): Container {
createContainer(options: IDiagramOptions): Container {
const container = this.diagramContainerFactory();
this.configureContainer(container, widgetId, ...this.getContainerConfiguration());
const diagramOptionsModule = this.createDiagramOptionsModule(options);
this.configureContainer(container, diagramOptionsModule, ...this.getContainerConfiguration());
this.initializeContainer(container);
return container;
}

protected createDiagramOptionsModule(options: IDiagramOptions): ContainerModule {
return createDiagramOptionsModule(options);
}

/**
* Retrieves additional {@link ContainerConfiguration} for the diagram container.
* Typically this composes a set of theia specific customization modules.
Expand All @@ -95,10 +100,9 @@ export abstract class GLSPDiagramConfiguration implements DiagramConfiguration {
* Theia specific bindings can be either be loaded as additional {@link ContainerConfiguration} or
* setup using the {@link configure} method.
* @param container The newly created DI container
* @param widgetId The id of the corresponding diagram widget.
* @param containerConfiguration Optional additional container configuration
*/
abstract configureContainer(container: Container, widgetId: string, ...containerConfiguration: ContainerConfiguration): void;
abstract configureContainer(container: Container, ...containerConfiguration: ContainerConfiguration): void;

protected initializeContainer(container: Container): void {
connectTheiaContextMenuService(container, this.contextMenuServiceFactory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
********************************************************************************/
// based on: https://github.com/eclipse-sprotty/sprotty-theia/blob/v0.12.0/src/theia/diagram-manager.ts

import { codiconCSSString, EditMode } from '@eclipse-glsp/client';
import { codiconCSSString, EditMode, IDiagramOptions } from '@eclipse-glsp/client';
import {
ApplicationShell,
FrontendApplicationContribution,
Expand Down Expand Up @@ -97,9 +97,9 @@ export abstract class GLSPDiagramManager extends WidgetOpenHandler<GLSPDiagramWi
}
if (options.mode === 'activate') {
await widget.getSvgElement();
await this.shell.activateWidget(widget.widgetId);
await this.shell.activateWidget(widget.id);
} else if (options.mode === 'reveal') {
await this.shell.revealWidget(widget.widgetId);
await this.shell.revealWidget(widget.id);
}
if (this.handleNavigations(widget, options)) {
return;
Expand Down Expand Up @@ -134,30 +134,37 @@ export abstract class GLSPDiagramManager extends WidgetOpenHandler<GLSPDiagramWi

async createWidget(options?: any): Promise<GLSPDiagramWidget> {
if (GLSPDiagramWidgetOptions.is(options)) {
const clientId = this.createClientId();
const widgetId = this.createWidgetId(options);
const config = this.getDiagramConfiguration(options);
const diContainer = config.createContainer(clientId);
const diagramOptions = this.createDiagramOptions(options);
const diContainer = config.createContainer(diagramOptions);
const glpsClientContribution = this.glspClientProvider.getGLSPClientContribution(this.contributionId);
if (!glpsClientContribution) {
throw new Error(`No glsp client contribution is registered for id: ${this.contributionId}!`);
}

const widget = new GLSPDiagramWidget(
options,
widgetId,
diContainer,
this.editorPreferences,
this.storage,
this.theiaSelectionService,
glpsClientContribution
);
const widget = new GLSPDiagramWidget(options, diContainer, this.editorPreferences, this.storage, this.theiaSelectionService);
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: async () => {
const client = await this.glspClientProvider.getGLSPClient(this.contributionId);
if (!client) {
throw new Error(`No glsp client is registered for id: ${this.contributionId}!`);
}
return client;
}
};
}

protected createClientId(): string {
return this.diagramType + '_' + this.widgetCount++;
}
Expand All @@ -173,10 +180,6 @@ export abstract class GLSPDiagramManager extends WidgetOpenHandler<GLSPDiagramWi
};
}

protected createWidgetId(options: GLSPDiagramWidgetOptions): string {
return `${this.diagramType}:${options.uri}`;
}

protected getDiagramConfiguration(options: GLSPDiagramWidgetOptions): DiagramConfiguration {
return this.diagramConfigurationRegistry.get(options.diagramType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ import {
AnyObject,
Args,
Bounds,
configureServerActions,
DiagramLoader,
EditorContextService,
EMPTY_ROOT,
EnableToolPaletteAction,
EndProgressAction,
FocusStateChangedAction,
FocusTracker,
GLSPActionDispatcher,
Expand All @@ -35,13 +33,8 @@ import {
RequestModelAction,
RequestTypeHintsAction,
SelectAction,
ServerStatusAction,
SetEditModeAction,
SetModelAction,
SetUIExtensionVisibilityAction,
SetViewportAction,
StartProgressAction,
StatusOverlay,
TYPES,
ViewerOptions,
Viewport
Expand All @@ -62,7 +55,6 @@ import URI from '@theia/core/lib/common/uri';
import { Container } from '@theia/core/shared/inversify';
import { EditorPreferences } from '@theia/editor/lib/browser';
import { pickBy } from 'lodash';
import { GLSPClientContribution } from '../glsp-client-contribution';
import { GLSPSaveable } from './glsp-saveable';

export interface GLSPDiagramWidgetOptions extends NavigatableWidgetOptions {
Expand Down Expand Up @@ -104,25 +96,27 @@ export class GLSPDiagramWidget extends BaseWidget implements SaveableSource, Sta

constructor(
protected options: GLSPDiagramWidgetOptions,
readonly widgetId: string,
readonly diContainer: Container,
readonly editorPreferences: EditorPreferences,
readonly storage: StorageService,
readonly theiaSelectionService: SelectionService,
readonly glspClientContribution: GLSPClientContribution
readonly theiaSelectionService: SelectionService
) {
super();
this.title.closable = true;
this.title.label = options.label;
this.title.iconClass = options.iconClass;
this.id = widgetId;
this.id = this.createWidgetId();
this.saveable = new GLSPSaveable(this.actionDispatcher, this.diContainer.get(EditorContextService));
this.updateSaveable();
this.title.caption = this.uri.path.fsPath();
this.toDispose.push(editorPreferences.onPreferenceChanged(() => this.updateSaveable()));
this.toDispose.push(this.saveable);
}

protected createWidgetId(): string {
return `${this.diagramType}:${this.options.uri}`;
}

protected override onAfterAttach(msg: Message): void {
if (!this.diagramContainer) {
// Create the container and initialize its content upon first attachment
Expand Down Expand Up @@ -158,38 +152,16 @@ export class GLSPDiagramWidget extends BaseWidget implements SaveableSource, Sta
}

protected async initializeDiagram(): Promise<void> {
await this.initializeGLSPClient();
// Filter options to only contain defined primitive values
const definedOptions: any = pickBy(this.options, v => v !== undefined && typeof v !== 'object');
this.requestModelOptions = {
sourceUri: this.uri.path.fsPath(),
...definedOptions
};

return this.dispatchInitialActions();
}

protected async initializeGLSPClient(): Promise<void> {
const modelSource = this.diContainer.get(GLSPModelSource);
// Dispatch a placeholder model until the real model from the server is available.
await this.actionDispatcher.dispatch(SetModelAction.create(EMPTY_ROOT));
// Initialize GLSP client
await this.actionDispatcher.dispatch(SetUIExtensionVisibilityAction.create({ extensionId: StatusOverlay.ID, visible: true }));
this.actionDispatcher.dispatch(ServerStatusAction.create('Initializing...', { severity: 'INFO' }));
this.actionDispatcher.dispatch(StartProgressAction.create({ progressId: 'initializeClient', title: 'Initializing' }));
const glspClient = await this.glspClientContribution.glspClient;
const initializeResult = glspClient.initializeResult;
if (!initializeResult) {
throw new Error('Error during diagram initialization. The GLSP server has not been initialized yet');
}
configureServerActions(initializeResult, this.diagramType, this.diContainer);
await glspClient.initializeClientSession({ clientSessionId: this.clientId, diagramType: this.options.diagramType });
modelSource.connect(glspClient, this.clientId);
this.disposed.connect(() => {
modelSource.dispose();
});
this.actionDispatcher.dispatch(ServerStatusAction.create('', { severity: 'NONE' }));
this.actionDispatcher.dispatch(EndProgressAction.create('initializeClient'));
const loader = this.diContainer.get(DiagramLoader);

return loader.load(this.requestModelOptions);
}

protected getBoundsInPage(element: Element): Bounds {
Expand Down
3 changes: 2 additions & 1 deletion packages/theia-integration/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"compilerOptions": {
"rootDir": "src",
"outDir": "lib",
"baseUrl": "."
"baseUrl": ".",
"module": "Node16"
},
"include": ["src"]
}
Loading

0 comments on commit b79696c

Please sign in to comment.