Skip to content

Commit

Permalink
Rework VS Code integration API
Browse files Browse the repository at this point in the history
This commit reworks a large part of the VS Code integration package by
updating the user-facing API and adding editor features.

At the heart of the user-facing API lies the "GlspVscodeConnector". This
component acts as a bridge between a diagram client and a graphical
language server implementation, both of which the extension authors
provide via the connector's options. The connector will propagate any
message sent by the server to the client and vice-versa. It intercepts
messages of certain types to add VS Code native functionality, for
example, it processes the "setMarkers" action sent by the server to
render diagnostics within VS Code.

The VS Code connector exposes methods to interact with active clients.
The "sendActionToActiveClient" method can be used to send various
GLSP-Protocol Actions to the currently active client. Extension authors
can use this action to interact with the diagram through user-triggered
VS Code commands. Extension authors can use interceptor functions in the
connector configuration to control how the VS Code integration behaves
when it receives messages.

The new package API now provides "quickstart components" that reduce the
necessary boilerplate code to get started. Quickstart components include
a helper class to launch the default GLSP language server, a default
editor provider to render webviews, and a helper class to connect a
language server to the VS Code integration.

Complete Changelog:

- Added native editor functionality
  - Clipboard support
  - SVG Export
  - Native menu contributions

- Rework API for extension authors
  - Component to bridge client and server (GlspVscodeConnector)
  - Interceptors
  - Method to interact with currently active diagram editor
  (sendActionToActiveClient)
  - Exposure of currently selected elements within the diagram editor

- Add quickstart components
  - GLSP server launcher
  - Default editor provider
  - Helper class to connect language server to VS Code integration

- Add documentation
  • Loading branch information
lforst committed Sep 27, 2021
1 parent a49cc81 commit e255f8b
Show file tree
Hide file tree
Showing 37 changed files with 2,040 additions and 1,228 deletions.
104 changes: 86 additions & 18 deletions example/workflow/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,60 +41,128 @@
],
"commands": [
{
"command": "workflow.diagram.fit",
"command": "workflow.fit",
"title": "Fit to Screen",
"category": "Workflow Diagram",
"enablement": "glsp-workflow-diagram-focused"
"enablement": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"command": "workflow.diagram.center",
"command": "workflow.center",
"title": "Center selection",
"category": "Workflow Diagram",
"enablement": "glsp-workflow-diagram-focused"
"enablement": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"command": "workflow.diagram.layout",
"command": "workflow.layout",
"title": "Layout diagram",
"category": "Workflow Diagram",
"enablement": "glsp-workflow-diagram-focused"
"enablement": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"command": "workflow.goToNextNode",
"title": "Go to next Node",
"category": "Workflow Navigation",
"enablement": "glsp-workflow-diagram-focused"
"enablement": "activeCustomEditorId == 'workflow.glspDiagram' && workflow.editorSelectedElementsAmount == 1"
},
{
"command": "workflow.goToPreviousNode",
"title": "Go to previous Node",
"category": "Workflow Navigation",
"enablement": "glsp-workflow-diagram-focused"
"enablement": "activeCustomEditorId == 'workflow.glspDiagram' && workflow.editorSelectedElementsAmount == 1"
},
{
"command": "workflow.showDocumentation",
"title": "Show documentation...",
"category": "Workflow Diagram",
"enablement": "glsp-workflow-diagram-focused"
"enablement": "activeCustomEditorId == 'workflow.glspDiagram' && workflow.editorSelectedElementsAmount == 1"
},
{
"command": "workflow.exportAsSVG",
"title": "Export as SVG",
"category": "Workflow Diagram",
"enablement": "activeCustomEditorId == 'workflow.glspDiagram'"
}
],
"submenus": [
{
"id": "workflow.editor.title",
"label": "Diagram"
}
],
"menus": {
"editor/title": [
{
"submenu": "workflow.editor.title",
"group": "bookmarks"
},
{
"command": "workflow.goToNextNode",
"group": "navigation",
"when": "activeCustomEditorId == 'workflow.glspDiagram' && workflow.editorSelectedElementsAmount == 1"
},
{
"command": "workflow.goToPreviousNode",
"group": "navigation",
"when": "activeCustomEditorId == 'workflow.glspDiagram' && workflow.editorSelectedElementsAmount == 1"
},
{
"command": "workflow.showDocumentation",
"group": "navigation",
"when": "activeCustomEditorId == 'workflow.glspDiagram' && workflow.editorSelectedElementsAmount == 1"
}
],
"workflow.editor.title": [
{
"command": "workflow.fit",
"group": "navigation",
"when": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"command": "workflow.center",
"group": "navigation",
"when": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"command": "workflow.layout",
"group": "navigation",
"when": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"command": "workflow.exportAsSVG",
"when": "activeCustomEditorId == 'workflow.glspDiagram'"
}
]
},
"keybindings": [
{
"key": "alt+f",
"mac": "alt+f",
"command": "workflow.diagram.fit",
"when": "glsp-workflow-diagram-focused"
"command": "workflow.fit",
"when": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"key": "alt+c",
"mac": "alt+c",
"command": "workflow.diagram.center",
"when": "glsp-workflow-diagram-focused"
"command": "workflow.center",
"when": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"key": "alt+l",
"mac": "alt+l",
"command": "workflow.diagram.layout",
"when": "glsp-workflow-diagram-focused"
"command": "workflow.layout",
"when": "activeCustomEditorId == 'workflow.glspDiagram'"
},
{
"key": "Ctrl+4",
"mac": "cmd+4",
"command": "workflow.goToNextNode",
"when": "activeCustomEditorId == 'workflow.glspDiagram' && workflow.editorSelectedElementsAmount == 1"
},
{
"key": "Ctrl+3",
"mac": "cmd+3",
"command": "workflow.goToPreviousNode",
"when": "activeCustomEditorId == 'workflow.glspDiagram' && workflow.editorSelectedElementsAmount == 1"
}
]
},
Expand All @@ -109,15 +177,15 @@
],
"main": "./lib/workflow-extension",
"devDependencies": {
"@eclipse-glsp/vscode-integration": "0.9.0",
"@types/node": "^8.0.0",
"path": "^0.12.7",
"rimraf": "^2.6.3",
"@eclipse-glsp/vscode-integration": "0.9.0",
"ts-loader": "^6.2.1",
"ts-node": "^9.1.1",
"workflow-glsp-webview": "0.9.0",
"typescript": "^3.1.3",
"vscode": "^1.1.21"
"vscode": "^1.1.21",
"workflow-glsp-webview": "0.9.0"
},
"scripts": {
"prepare": "npx vscode-install && yarn clean && yarn build && yarn lint",
Expand Down
64 changes: 64 additions & 0 deletions example/workflow/extension/src/workflow-editor-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/********************************************************************************
* Copyright (c) 2021 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.01
********************************************************************************/

import * as vscode from 'vscode';
import * as path from 'path';

import { GlspVscodeConnector } from '@eclipse-glsp/vscode-integration';
import { GlspEditorProvider } from '@eclipse-glsp/vscode-integration/lib/quickstart-components';

export default class WorkflowEditorProvider extends GlspEditorProvider {
diagramType = 'workflow-diagram';

constructor(
protected readonly extensionContext: vscode.ExtensionContext,
protected readonly glspVscodeConnector: GlspVscodeConnector
) {
super(glspVscodeConnector);
}

setUpWebview(_document: vscode.CustomDocument, webviewPanel: vscode.WebviewPanel, _token: vscode.CancellationToken, clientId: string): void {
const localResourceRootsUri = vscode.Uri.file(
path.join(this.extensionContext.extensionPath, './pack')
);

const webviewScriptSourceUri = vscode.Uri.file(
path.join(this.extensionContext.extensionPath, './pack/webview.js')
);

webviewPanel.webview.options = {
localResourceRoots: [localResourceRootsUri],
enableScripts: true
};

webviewPanel.webview.html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, height=device-height">
<link
rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
crossorigin="anonymous">
</head>
<body>
<div id="${clientId}_container" style="height: 100%;"></div>
<script src="${webviewPanel.webview.asWebviewUri(webviewScriptSourceUri).toString()}"></script>
</body>
</html>`;
}
}
98 changes: 80 additions & 18 deletions example/workflow/extension/src/workflow-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,99 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { GlspDiagramEditorContext, NavigateAction } from '@eclipse-glsp/vscode-integration';
import { join, resolve } from 'path';

import * as vscode from 'vscode';
import * as process from 'process';
import * as path from 'path';

import {
GlspVscodeConnector,
NavigateAction,
LayoutOperation,
FitToScreenAction,
CenterAction,
RequestExportSvgAction
} from '@eclipse-glsp/vscode-integration';

import {
GlspServerLauncher,
SocketGlspVscodeServer
} from '@eclipse-glsp/vscode-integration/lib/quickstart-components';

import WorkflowEditorProvider from './workflow-editor-provider';

const DEFAULT_SERVER_PORT = '5007';

import { WorkflowGlspDiagramEditorContext } from './workflow-glsp-diagram-editor-context';
export async function activate(context: vscode.ExtensionContext): Promise<void> {
// Start server process using quickstart component
if (process.env.GLSP_SERVER_DEBUG !== 'true') {
const serverProcess = new GlspServerLauncher({
jarPath: path.join(__dirname, '../server/org.eclipse.glsp.example.workflow-0.9.0-SNAPSHOT-glsp.jar'),
serverPort: JSON.parse(process.env.GLSP_SERVER_PORT || DEFAULT_SERVER_PORT),
additionalArgs: ['--fileLog', 'true', '--logDir', path.join(__dirname, '../server')],
logging: true
});
context.subscriptions.push(serverProcess);
await serverProcess.start();
}

// Wrap server with quickstart component
const workflowServer = new SocketGlspVscodeServer({
clientId: 'glsp.workflow',
clientName: 'workflow',
serverPort: JSON.parse(process.env.GLSP_SERVER_PORT || DEFAULT_SERVER_PORT)
});

export const SERVER_DIR = join(__dirname, '..', 'server');
export const JAR_FILE = resolve(join(SERVER_DIR, 'org.eclipse.glsp.example.workflow-0.9.0-SNAPSHOT-glsp.jar'));
// Initialize GLSP-VSCode connector with server wrapper
const glspVscodeConnector = new GlspVscodeConnector({
server: workflowServer,
logging: true
});

let editorContext: GlspDiagramEditorContext;
const customEditorProvider = vscode.window.registerCustomEditorProvider(
'workflow.glspDiagram',
new WorkflowEditorProvider(context, glspVscodeConnector),
{
webviewOptions: { retainContextWhenHidden: true },
supportsMultipleEditorsPerDocument: false
}
);

export function activate(context: vscode.ExtensionContext): void {
editorContext = new WorkflowGlspDiagramEditorContext(context);
context.subscriptions.push(workflowServer, glspVscodeConnector, customEditorProvider);
workflowServer.start();

// Keep track of selected elements
let selectedElements: string[] = [];
context.subscriptions.push(
glspVscodeConnector.onSelectionUpdate(n => {
selectedElements = n;
vscode.commands.executeCommand('setContext', 'workflow.editorSelectedElementsAmount', n.length);
})
);

// Register various commands
context.subscriptions.push(
vscode.commands.registerCommand('workflow.fit', () => {
glspVscodeConnector.sendActionToActiveClient(new FitToScreenAction(selectedElements));
}),
vscode.commands.registerCommand('workflow.center', () => {
glspVscodeConnector.sendActionToActiveClient(new CenterAction(selectedElements));
}),
vscode.commands.registerCommand('workflow.layout', () => {
glspVscodeConnector.sendActionToActiveClient(new LayoutOperation());
}),
vscode.commands.registerCommand('workflow.goToNextNode', () => {
editorContext.dispatchActionToWebview(new NavigateAction('next'));
glspVscodeConnector.sendActionToActiveClient(new NavigateAction('next'));
}),
vscode.commands.registerCommand('workflow.goToPreviousNode', () => {
editorContext.dispatchActionToWebview(new NavigateAction('previous'));
glspVscodeConnector.sendActionToActiveClient(new NavigateAction('previous'));
}),
vscode.commands.registerCommand('workflow.showDocumentation', () => {
editorContext.dispatchActionToWebview(new NavigateAction('documentation'));
glspVscodeConnector.sendActionToActiveClient(new NavigateAction('documentation'));
}),
vscode.commands.registerCommand('workflow.exportAsSVG', () => {
glspVscodeConnector.sendActionToActiveClient(new RequestExportSvgAction());
})
);
}

export function deactivate(): Thenable<void> {
if (!editorContext) {
return Promise.resolve(undefined);
}
return editorContext.deactivateGLSPClient();
}

Loading

0 comments on commit e255f8b

Please sign in to comment.