Skip to content

Commit

Permalink
Introduce GLSPModelSource
Browse files Browse the repository at this point in the history
- Deprecate the `GLSPDiagramServer` model source and introduce a new `GLSPModelSource`.
In contrast to the `GLSPDiagramServer` the new model source is only responsible for sending actions to and receiving actions for the GLSP server.
It no longer serves as dedicated action handler. In addition, to a cleaner separation of concerns this also enables using a generic `ModelSource` for all
platform integrations i.e. we no longer have the separation between `GLSPDiagramServer´ and `GLSPTheiaDiagramServer`
- Deprecate the `SourceUriAware` interface
- Introduce dedicated action handlers for `ExportSvgAction`, `ServerMessageAction` and `ServerStatusAction`
- Add a fallback dirty state action handler for the standalone example
- Extend the `createContainer` function of the workflowDiagram so that it takes additional customModules as rest parameter

Part of eclipse-glsp/glsp#900
  • Loading branch information
tortmayr committed Mar 16, 2023
1 parent fa3f9e6 commit 35e91f8
Show file tree
Hide file tree
Showing 20 changed files with 275 additions and 41 deletions.
5 changes: 3 additions & 2 deletions examples/workflow-glsp/src/di.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
GLSPProjectionView,
GridSnapper,
LogLevel,
ModuleConfiguration,
overrideViewerOptions,
RectangularNodeView,
RevealNamedElementActionProvider,
Expand Down Expand Up @@ -75,8 +76,8 @@ const workflowDiagramModule = new ContainerModule((bind, unbind, isBound, rebind
configureModelElement(context, 'struct', SCompartment, StructureCompartmentView);
});

export default function createContainer(widgetId: string): Container {
const container = createDiagramContainer(workflowDiagramModule, directTaskEditor);
export default function createContainer(widgetId: string, ...customModules: Array<ContainerModule | ModuleConfiguration>): Container {
const container = createDiagramContainer(workflowDiagramModule, directTaskEditor, ...customModules);
overrideViewerOptions(container, {
baseDiv: widgetId,
hiddenDiv: widgetId + '_hidden'
Expand Down
1 change: 1 addition & 0 deletions examples/workflow-standalone/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"path-browserify": "^1.0.1",
"source-map-loader": "^4.0.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.4.2",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
},
Expand Down
13 changes: 6 additions & 7 deletions examples/workflow-standalone/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ import {
EnableToolPaletteAction,
GLSPActionDispatcher,
GLSPClient,
GLSPDiagramServer,
listen,
GLSPModelSource,
RequestModelAction,
RequestTypeHintsAction,
TYPES
RequestTypeHintsAction
} from '@eclipse-glsp/client';
import { join, resolve } from 'path';
import { MessageConnection } from 'vscode-jsonrpc';
Expand All @@ -40,15 +39,15 @@ const examplePath = resolve(join(currentDir, '../app/example1.wf'));
const clientId = ApplicationIdProvider.get() + '_' + examplePath;

const container = createContainer();
const diagramServer = container.get<GLSPDiagramServer>(TYPES.ModelSource);
diagramServer.clientId = clientId;
const modelSource = container.get(GLSPModelSource);
modelSource.clientId = clientId;

listen(websocket, connection => initialize(connection));

async function initialize(connectionProvider: MessageConnection): Promise<void> {
const client = new BaseJsonrpcGLSPClient({ id, connectionProvider });

await diagramServer.connect(client);
await modelSource.connect(client);
const result = await client.initializeServer({
applicationId: ApplicationIdProvider.get(),
protocolVersion: GLSPClient.protocolVersion
Expand All @@ -57,7 +56,7 @@ async function initialize(connectionProvider: MessageConnection): Promise<void>

const actionDispatcher = container.get(GLSPActionDispatcher);

await client.initializeClientSession({ clientSessionId: diagramServer.clientId, diagramType });
await client.initializeClientSession({ clientSessionId: modelSource.clientId, diagramType });
actionDispatcher.dispatch(
RequestModelAction.create({
options: {
Expand Down
40 changes: 30 additions & 10 deletions examples/workflow-standalone/src/di.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2019-2022 EclipseSource and others.
* Copyright (c) 2019-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,16 +14,36 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { createWorkflowDiagramContainer } from '@eclipse-glsp-examples/workflow-glsp';
import { ConsoleLogger, GLSPDiagramServer, LogLevel, TYPES } from '@eclipse-glsp/client';
import { Container } from 'inversify';
import {
Action,
configureActionHandler,
IActionHandler,
ILogger,
ServerMessageAction,
ServerStatusAction,
SetDirtyStateAction,
TYPES
} from '@eclipse-glsp/client';
import { Container, ContainerModule, inject, injectable } from 'inversify';
import '../css/diagram.css';

@injectable()
class LoggingActionHandler implements IActionHandler {
@inject(TYPES.ILogger)
protected logger: ILogger;

handle(action: Action): void {
this.logger.log(this, 'Received action:', action);
}
}

const standaloneModule = new ContainerModule((bind, unbind, isBound, rebind) => {
bind(TYPES.IMarqueeBehavior).toConstantValue({ entireEdge: true, entireElement: true });
configureActionHandler({ bind, isBound }, SetDirtyStateAction.KIND, LoggingActionHandler);
configureActionHandler({ bind, isBound }, ServerStatusAction.KIND, LoggingActionHandler);
configureActionHandler({ bind, isBound }, ServerMessageAction.KIND, LoggingActionHandler);
});

export default function createContainer(): Container {
const container = createWorkflowDiagramContainer('sprotty');
container.bind(GLSPDiagramServer).toSelf().inSingletonScope();
container.bind(TYPES.ModelSource).toService(GLSPDiagramServer);
container.rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope();
container.rebind(TYPES.LogLevel).toConstantValue(LogLevel.warn);
container.bind(TYPES.IMarqueeBehavior).toConstantValue({ entireEdge: true, entireElement: true });
return container;
return createWorkflowDiagramContainer('sprotty', standaloneModule);
}
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"sprotty": "0.13.0-next.f4445dd.342"
},
"devDependencies": {
"@types/file-saver": "^2.0.3",
"@vscode/codicons": "^0.0.25"
},
"publishConfig": {
Expand Down
2 changes: 0 additions & 2 deletions packages/client/src/base/container-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
fadeModule,
labelEditUiModule,
modelSourceModule,
openModule,
routingModule,
zorderModule
} from 'sprotty';
Expand Down Expand Up @@ -76,7 +75,6 @@ export const DEFAULT_MODULES = [
modelSourceModule,
sourceModelWatcherModule,
navigationModule,
openModule,
toolPaletteModule,
routingModule,
toolFeedbackModule,
Expand Down
26 changes: 23 additions & 3 deletions packages/client/src/base/di.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,20 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { bindAsService, bindOrRebind, InitializeResult, SetEditModeAction } from '@eclipse-glsp/protocol';
import { Action, bindAsService, bindOrRebind, InitializeResult, SetEditModeAction } from '@eclipse-glsp/protocol';
import '@vscode/codicons/dist/codicon.css';
import { Container, ContainerModule } from 'inversify';
import { ActionHandlerRegistry, configureActionHandler, configureCommand, ModelSource, SetModelCommand } from 'sprotty';
import { Container, ContainerModule, injectable } from 'inversify';
import {
ActionHandlerRegistry,
configureActionHandler,
configureCommand,
IActionHandler,
ModelSource,
SetModelCommand,
SwitchEditModeAction
} from 'sprotty';
import '../../css/glsp-sprotty.css';
import { GLSPModelSource } from '../model-source/glsp-model-source';
import { GLSPActionDispatcher } from './action-dispatcher';
import { FocusStateChangedAction } from './actions/focus-change-action';
import { GLSPCommandStack } from './command-stack';
Expand Down Expand Up @@ -67,8 +76,19 @@ const defaultGLSPModule = new ContainerModule((bind, _unbind, isBound, rebind) =
// support re-registration of model elements and views
bindOrRebind(context, TYPES.SModelRegistry).to(GLSPModelRegistry).inSingletonScope();
bindOrRebind(context, TYPES.ViewRegistry).to(GLSPViewRegistry).inSingletonScope();
// configure a no-op ActionHandler for `SwitchEditModeAction`s. They are dispatched from sprotty but
configureActionHandler(context, SwitchEditModeAction.KIND, NoOpSwitchEditModeActionHandler);

bindAsService(bind, TYPES.ModelSource, GLSPModelSource);
});

@injectable()
class NoOpSwitchEditModeActionHandler implements IActionHandler {
handle(action: Action): void {
// no-op
}
}

export default defaultGLSPModule;

/**
Expand Down
7 changes: 2 additions & 5 deletions packages/client/src/base/editor-context-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Action, Args, distinctAdd, EditMode, EditorContext, remove, SetEditMode
import { inject, injectable, multiInject, optional } from 'inversify';
import { IActionHandler, ModelSource, MousePositionTracker, SModelElement, SModelRoot } from 'sprotty';
import { SelectionService } from '../features/select/selection-service';
import { isSourceUriAware } from './source-uri-aware';
import { GLSPModelSource } from '../model-source/glsp-model-source';
import { TYPES } from './types';

export interface EditModeListener {
Expand Down Expand Up @@ -91,10 +91,7 @@ export class EditorContextService implements IActionHandler {

async getSourceUri(): Promise<string | undefined> {
const modelSource = await this.modelSourceProvider();
if (isSourceUriAware(modelSource)) {
return modelSource.sourceURI;
}
return undefined;
return modelSource instanceof GLSPModelSource ? modelSource.sourceUri : undefined;
}

get editMode(): string {
Expand Down
10 changes: 9 additions & 1 deletion packages/client/src/base/source-uri-aware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2020-2022 EclipseSource and others.
* Copyright (c) 2019-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,10 +14,18 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { AnyObject, hasStringProp } from '@eclipse-glsp/protocol';

/**
* @deprecated No longer in use. The GLSP model source types have been unified to `GLSPModelsource` so there is no more
* need for this interface.
*/
export interface SourceUriAware {
sourceURI: string;
}

/**
* @deprecated The type of this guard check is deprecated (`SourceURIAware`)
*/
export function isSourceUriAware(obj: any): obj is SourceUriAware {
return AnyObject.is(obj) && hasStringProp(obj, 'sourceURI');
}
5 changes: 4 additions & 1 deletion packages/client/src/features/export/di.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { bindAsService } from '@eclipse-glsp/protocol';
import { ExportSvgAction } from '@eclipse-glsp/protocol';
import { ContainerModule } from 'inversify';
import { configureCommand, ExportSvgCommand, ExportSvgKeyListener, ExportSvgPostprocessor, TYPES } from 'sprotty';
import { configureActionHandler, configureCommand, ExportSvgCommand, ExportSvgKeyListener, ExportSvgPostprocessor, TYPES } from 'sprotty';
import { ExportSvgActionHandler } from './export-svg-action-handler';
import { GLSPSvgExporter } from './glsp-svg-exporter';

const glspExportSvgModule = new ContainerModule((bind, _unbind, isBound) => {
Expand All @@ -24,6 +26,7 @@ const glspExportSvgModule = new ContainerModule((bind, _unbind, isBound) => {
bindAsService(context, TYPES.HiddenVNodePostprocessor, ExportSvgPostprocessor);
configureCommand(context, ExportSvgCommand);
bind(TYPES.SvgExporter).to(GLSPSvgExporter).inSingletonScope();
configureActionHandler({ bind, isBound }, ExportSvgAction.KIND, ExportSvgActionHandler);
});

export default glspExportSvgModule;
31 changes: 31 additions & 0 deletions packages/client/src/features/export/export-svg-action-handler.ts
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 { saveAs } from 'file-saver';
import { injectable } from 'inversify';
import { ExportSvgAction, IActionHandler } from 'sprotty';

@injectable()
/**
* The default handler for {@link ExportSvgAction}s. Usually
* this handler is rebound to an application specific handler in platform integration modules
* (e.g. the Theia integration)
*/
export class ExportSvgActionHandler implements IActionHandler {
handle(action: ExportSvgAction): void {
const blob = new Blob([action.svg], { type: 'text/plain;charset=utf-8' });
saveAs(blob, 'diagram.svg');
}
}
4 changes: 2 additions & 2 deletions packages/client/src/features/mouse-tool/mouse-tool.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2019-2022 EclipseSource and others.
* Copyright (c) 2019-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 Down Expand Up @@ -82,7 +82,7 @@ export class RankingMouseTool extends MouseTool implements IMouseTool {
if (Action.is(actionOrPromise)) {
await this.actionDispatcher.dispatch(actionOrPromise);
} else {
actionOrPromise.then((action: Action) => {
(actionOrPromise as Promise<Action>).then((action: Action) => {
this.actionDispatcher.dispatch(action);
});
}
Expand Down
2 changes: 2 additions & 0 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export * from './features/copy-paste/copy-paste-handler';
export * from './features/decoration/decoration-placer';
export * from './features/edit-label/edit-label-tool';
export * from './features/edit-label/edit-label-validator';
export * from './features/export/export-svg-action-handler';
export * from './features/export/glsp-svg-exporter';
export * from './features/hints/model';
export * from './features/hints/type-hints';
Expand Down Expand Up @@ -119,6 +120,7 @@ export * from './features/viewport/glsp-scroll-mouse-listener';
// ------------------ Misc ------------------
export * from './lib/model';
export * from './model-source/glsp-diagram-server';
export * from './model-source/glsp-model-source';
export * from './sprotty-reexport';
export * from './utils/argument-utils';
export * from './utils/html-utils';
Expand Down
13 changes: 12 additions & 1 deletion packages/client/src/model-source/glsp-diagram-server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2019-2022 EclipseSource and others.
* Copyright (c) 2019-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 @@ -13,6 +13,8 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
/* eslint-disable deprecation/deprecation */
/* eslint-disable import/no-deprecated */
import {
Action,
ActionMessage,
Expand All @@ -28,6 +30,9 @@ import { SourceUriAware } from '../base/source-uri-aware';

const receivedFromServerProperty = '__receivedFromServer';

/**
* @deprecated Use `GLSPModelSource` instead
*/
@injectable()
export class GLSPDiagramServer extends DiagramServerProxy implements SourceUriAware {
protected _sourceUri: string;
Expand Down Expand Up @@ -95,10 +100,16 @@ export class GLSPDiagramServer extends DiagramServerProxy implements SourceUriAw
}
}

/**
* @deprecated Use `Action.origin` instead
*/
export function isReceivedFromServer(action: Action): boolean {
return (action as any)[receivedFromServerProperty] === true;
}

/**
* @deprecated Manual registration of actions for the {@link DiagramServerProxy} is discouraged.
*/
export function registerDefaultGLSPServerActions(registry: ActionHandlerRegistry, diagramServer: DiagramServerProxy): void {
registry.register(ServerMessageAction.KIND, diagramServer);
registry.register(ServerStatusAction.KIND, diagramServer);
Expand Down
Loading

0 comments on commit 35e91f8

Please sign in to comment.