Skip to content

Commit

Permalink
GLSP-960: Improve Diagram container configuration (#51)
Browse files Browse the repository at this point in the history
* GLSP-960: Improve Diagram container configuration 

- Use vscode-messenger protocol for webview-extension communication
- Refactor webview communication code into `WebviewEndpoint` class
- Introduce a `WebviewGLSPClient` that forwards RPC calls to the GLSP client running in the host extension
- Refactor and update codebase to conform to changes from GLSP-960
Part of eclipse-glsp/glsp/issues/960
Requires eclipse-glsp/glsp-client#282
  • Loading branch information
tortmayr committed Sep 4, 2023
1 parent 7f81443 commit e5a109d
Show file tree
Hide file tree
Showing 25 changed files with 1,339 additions and 1,010 deletions.
4 changes: 3 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
"${workspaceRoot}/example/workflow/extension/dist/workflow-extension.js",
"${workspaceRoot}/example/workflow/extension/lib/**/*.js",
"${workspaceRoot}/packages/vscode-integration/lib/**/*.js",
"${workspaceRoot}/node_modules/@eclipse-glsp/*/lib/**/*.js"
"${workspaceRoot}/node_modules/@eclipse-glsp/*/lib/**/*.js",
"${workspaceRoot}/node_modules/vscode-messenger/*/lib/**/*.js",
"${workspaceRoot}/node_modules/vscode-messenger-common/*/lib/**/*.js"
],
"sourceMaps": true,
"sourceMapPathOverrides": {
Expand Down
7 changes: 4 additions & 3 deletions example/workflow/webview/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { createWorkflowDiagramContainer } from '@eclipse-glsp-examples/workflow-glsp';
import { GLSPDiagramIdentifier, GLSPStarter } from '@eclipse-glsp/vscode-integration-webview';
import { ContainerConfiguration } from '@eclipse-glsp/client';
import { GLSPStarter } from '@eclipse-glsp/vscode-integration-webview';
import '@eclipse-glsp/vscode-integration-webview/css/glsp-vscode.css';
import '@vscode/codicons/dist/codicon.css';
import { Container } from 'inversify';

class WorkflowGLSPStarter extends GLSPStarter {
createContainer(diagramIdentifier: GLSPDiagramIdentifier): Container {
return createWorkflowDiagramContainer(diagramIdentifier.clientId);
createContainer(...containerConfiguration: ContainerConfiguration): Container {
return createWorkflowDiagramContainer(...containerConfiguration);
}
}

Expand Down
1 change: 1 addition & 0 deletions example/workflow/workspace/d

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion packages/vscode-integration-webview/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"watch": "tsc -w"
},
"dependencies": {
"@eclipse-glsp/client": "next"
"@eclipse-glsp/client": "next",
"vscode-messenger-webview": "^0.4.5"
},
"devDependencies": {
"reflect-metadata": "^0.1.13"
Expand Down
27 changes: 27 additions & 0 deletions packages/vscode-integration-webview/src/default-modules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/********************************************************************************
* Copyright (c) 2023 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
********************************************************************************/

import { ModuleConfiguration } from '@eclipse-glsp/client';
import { vscodeCopyPasteModule } from './features/copyPaste/copy-paste-module';
import { vscodeDefaultModule } from './features/default/default-module';
import { vscodeExportModule } from './features/export/export-module';
import { vscodeNavigationModule } from './features/navigation/navigation-module';

export const VSCODE_DEFAULT_MODULES = [vscodeDefaultModule, vscodeCopyPasteModule, vscodeExportModule, vscodeNavigationModule] as const;

export const VSCODE_DEFAULT_MODULE_CONFIG: ModuleConfiguration = {
add: [...VSCODE_DEFAULT_MODULES]
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { AnyObject, hasStringProp, InitializeResult } from '@eclipse-glsp/client';
import { AnyObject, hasStringProp } from '@eclipse-glsp/client';

export const GLSPDiagramIdentifier = Symbol('GLSPDiagramIdentifier');

export interface GLSPDiagramIdentifier {
clientId: string;
diagramType: string;
uri: string;
initializeResult?: InitializeResult;
}

export function isDiagramIdentifier(object: any): object is GLSPDiagramIdentifier {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/********************************************************************************
* Copyright (c) 2023 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
********************************************************************************/

import {
FeatureModule,
ICopyPasteHandler,
RequestClipboardDataAction,
SetClipboardDataAction,
TYPES,
bindAsService,
copyPasteModule
} from '@eclipse-glsp/client';
import { ExtensionActionKind } from '../default/extension-action-handler';
import { CopyPasteHandlerProvider, CopyPasteStartup } from './copy-paste-startup';

export const vscodeCopyPasteModule = new FeatureModule(
bind => {
bind(CopyPasteHandlerProvider).toProvider(
ctx => () => new Promise<ICopyPasteHandler>(resolve => resolve(ctx.container.get<ICopyPasteHandler>(TYPES.ICopyPasteHandler)))
);
bindAsService(bind, TYPES.IDiagramStartup, CopyPasteStartup);
bind(ExtensionActionKind).toConstantValue(RequestClipboardDataAction.KIND);
bind(ExtensionActionKind).toConstantValue(SetClipboardDataAction.KIND);
},
{ requires: copyPasteModule }
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/********************************************************************************
* Copyright (c) 2023 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
********************************************************************************/

import { ICopyPasteHandler, IDiagramStartup, MaybePromise } from '@eclipse-glsp/client';
import { inject, injectable } from 'inversify';

export const CopyPasteHandlerProvider = Symbol('CopyPasteHandlerProvider');
/**
* Service identifier and type definition to bind the {@link ICopyPasteHandler} to an provider.
* This enables asynchronous injections and can be used to bypass circular dependency issues.
*/
export type CopyPasteHandlerProvider = () => Promise<ICopyPasteHandler>;

/**
* Startup service to hook up the copy&paste event handler
*/
@injectable()
export class CopyPasteStartup implements IDiagramStartup {
@inject(CopyPasteHandlerProvider)
protected copyPasteHandlerProvider: CopyPasteHandlerProvider;

preInitialize(): MaybePromise<void> {
this.copyPasteHandlerProvider().then(copyPasteHandler => {
document.addEventListener('copy', (e: ClipboardEvent) => {
copyPasteHandler.handleCopy(e);
});

document.addEventListener('cut', (e: ClipboardEvent) => {
copyPasteHandler.handleCut(e);
});

document.addEventListener('paste', (e: ClipboardEvent) => {
copyPasteHandler.handlePaste(e);
});
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/********************************************************************************
* Copyright (c) 2023 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
********************************************************************************/

import { FeatureModule, GLSPModelSource, TYPES, bindAsService, defaultModule } from '@eclipse-glsp/client';
import { GLSPDiagramWidget, GLSPDiagramWidgetFactory } from '../../glsp-diagram-widget';
import { HostExtensionActionHandler } from './extension-action-handler';
import { VsCodeGLSPModelSource } from './vscode-glsp-modelsource';

export const vscodeDefaultModule = new FeatureModule(
(bind, _unbind, _isBound, rebind) => {
bind(GLSPDiagramWidget).toSelf().inSingletonScope();
bind(GLSPDiagramWidgetFactory).toFactory(context => () => context.container.get<GLSPDiagramWidget>(GLSPDiagramWidget));
bind(VsCodeGLSPModelSource).toSelf().inSingletonScope();
rebind(GLSPModelSource).toService(VsCodeGLSPModelSource);
bindAsService(bind, TYPES.IActionHandlerInitializer, HostExtensionActionHandler);
},
{ requires: defaultModule }
);
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,48 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { Action, ActionHandlerRegistry, IActionHandler, IActionHandlerInitializer, ICommand } from '@eclipse-glsp/client';
import { GLSPDiagramIdentifier } from './diagram-identifier';
import { VsCodeApi } from './services';
import {
Action,
ActionHandlerRegistry,
GLSPClient,
IActionHandler,
IActionHandlerInitializer,
ICommand,
IDiagramOptions,
TYPES
} from '@eclipse-glsp/client';
import { inject, injectable, multiInject, optional, postConstruct } from 'inversify';

/**
* Delegates actions that should be handled inside of the glsp vscode extension instead
* Service identifier to define action kinds that should be delegated to the vscode extension.
*
* Usage:
* ```ts
* bind(ExtensionActionKind).toConstantValue(SomeAction.KIND)
* ```
*/
export const ExtensionActionKind = Symbol('ExtensionActionKind');

/**
* Delegates actions that should be handled inside of the glsp host extension instead
* of the webview. This enables the implementation of action handlers that require access
* to the vscode API and/or node backend.
*/
export class GLSPVscodeExtensionActionHandler implements IActionHandler, IActionHandlerInitializer {
constructor(
protected readonly actionKinds: string[],
protected readonly diagramIdentifier: GLSPDiagramIdentifier,
protected vscodeApi: VsCodeApi
) {}
@injectable()
export class HostExtensionActionHandler implements IActionHandler, IActionHandlerInitializer {
@multiInject(ExtensionActionKind)
@optional()
protected actionKinds: string[] = [];

@inject(TYPES.IDiagramOptions)
protected diagramOptions: IDiagramOptions;

protected glspClient?: GLSPClient;

@postConstruct()
protected postConstruct(): void {
this.diagramOptions.glspClientProvider().then(glspClient => (this.glspClient = glspClient));
}

initialize(registry: ActionHandlerRegistry): void {
this.actionKinds.forEach(kind => registry.register(kind, this));
Expand All @@ -36,11 +63,11 @@ export class GLSPVscodeExtensionActionHandler implements IActionHandler, IAction
handle(action: Action): void | Action | ICommand {
if (this.actionKinds.includes(action.kind)) {
const message = {
clientId: this.diagramIdentifier.clientId,
clientId: this.diagramOptions.clientId,
action,
__localDispatch: true
};
this.vscodeApi.postMessage(message);
this.glspClient?.sendActionMessage(message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/********************************************************************************
* Copyright (c) 2023 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
********************************************************************************/
import { Action, ActionMessage, GLSPModelSource, ServerAction } from '@eclipse-glsp/client';
import { injectable } from 'inversify';

/**
* A helper interface that allows the webview to mark actions that have been received directly from the vscode extension
* (i.e. they are not forwarded to the GLSP Server).
*/
export interface ExtensionAction extends Action {
__localDispatch: true;
}

export namespace ExtensionAction {
export function is(object: unknown): object is ExtensionAction {
return Action.is(object) && '__localDispatch' in object && object.__localDispatch === true;
}

/**
* Mark the given action as {@link ServerAction} by attaching the "_receivedFromServer" property
* @param action The action that should be marked as server action
*/
export function mark(action: Action): void {
(action as ExtensionAction).__localDispatch = true;
}
}

/**
* Customization of the default {@link GLSPModelSource} for the vscode integration.
* Also takes locally dispatched actions (i.e. actions that are originating from or intended for the host extension) into consideration.
*/
@injectable()
export class VsCodeGLSPModelSource extends GLSPModelSource {
protected override messageReceived(message: ActionMessage): void {
if (this.clientId !== message.clientId) {
return;
}
this.checkMessageOrigin(message);
const action = message.action;
this.logger.log(this, 'receiving', action);
this.actionDispatcher.dispatch(action);
}

protected checkMessageOrigin(message: ActionMessage): void {
const isLocalDispatch = (message as any)['__localDispatch'] ?? false;
if (!isLocalDispatch) {
ServerAction.mark(message.action);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/********************************************************************************
* Copyright (c) 2022 TypeFox and others.
* Modifications: 2023 EclipseSource and others.
* Copyright (c) 2023 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
Expand All @@ -14,13 +13,10 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
// based on https://github.com/eclipse-sprotty/sprotty-vscode/blob/v0.3.0/sprotty-vscode-webview/src/services.ts
export interface VsCodeApi {
postMessage: (message: any) => void;
}

export const VsCodeApi = Symbol('VsCodeApi');
import { ExportSvgAction, FeatureModule } from '@eclipse-glsp/client';
import { ExtensionActionKind } from '../default/extension-action-handler';

export interface GLSPStarterServices {
vscodeApi?: VsCodeApi;
}
export const vscodeExportModule = new FeatureModule(bind => {
bind(ExtensionActionKind).toConstantValue(ExportSvgAction.KIND);
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2022 EclipseSource and others.
* Copyright (c) 2023 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
Expand All @@ -14,11 +14,9 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ICopyPasteHandler } from '@eclipse-glsp/client';
import { FeatureModule, NavigateToExternalTargetAction, navigationModule } from '@eclipse-glsp/client';
import { ExtensionActionKind } from '../default/extension-action-handler';

export const CopyPasteHandlerProvider = Symbol('CopyPasteHandlerProvider');
/**
* Service identifier and type definition to bind the {@link ICopyPasteHandler} to an provider.
* This enables asynchronous injections and can be used to bypass circular dependency issues.
*/
export type CopyPasteHandlerProvider = () => Promise<ICopyPasteHandler>;
export const vscodeNavigationModule = new FeatureModule(bind => {
bind(ExtensionActionKind).toConstantValue(NavigateToExternalTargetAction.KIND);
}, navigationModule);

0 comments on commit e5a109d

Please sign in to comment.