From 7ea3c0ad555af7e7174403307ea945bbbd198210 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Thu, 22 Aug 2019 14:51:59 -0400 Subject: [PATCH] WIP --- .../languageserver/Sts4LanguageServer.java | 6 + .../sprotty/SprottyService.java | 12 + .../commons/protocol/SprottyClient.java | 12 + headless-services/commons/pom.xml | 1 + .../spring-boot-language-server/build.sh | 6 +- .../boot/app/diagram/PopupModelFactory.java | 25 +- .../sprotty-live-beans-client/copy-script.sh | 5 + .../sprotty-live-beans-client/css/diagram.css | 46 ++++ .../sprotty-live-beans-client/css/page.css | 38 +++ .../sprotty-live-beans-client/index.html | 39 +++ .../sprotty-live-beans-client/package.json | 33 +++ .../sprotty-live-beans-client/src/app.ts | 29 ++ .../src/di.config.ts | 78 ++++++ .../sprotty-live-beans-client/src/model.ts | 67 +++++ .../src/standalone.ts | 118 ++++++++ .../sprotty-live-beans-client/src/views.tsx | 107 ++++++++ .../sprotty-live-beans-client/tsconfig.json | 33 +++ .../webpack.config.js | 53 ++++ .../vscode-spring-boot/.gitignore | 3 +- .../vscode-spring-boot/lib/Main.ts | 9 +- .../vscode-spring-boot/lib/live-bean-view.ts | 255 ++++++++++++++++++ .../vscode-spring-boot/package.json | 17 +- .../vscode-spring-boot/tsconfig.json | 3 +- 23 files changed, 973 insertions(+), 22 deletions(-) create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/sprotty/SprottyService.java create mode 100644 headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/SprottyClient.java create mode 100644 nodejs-packages/sprotty-live-beans-client/copy-script.sh create mode 100644 nodejs-packages/sprotty-live-beans-client/css/diagram.css create mode 100644 nodejs-packages/sprotty-live-beans-client/css/page.css create mode 100644 nodejs-packages/sprotty-live-beans-client/index.html create mode 100644 nodejs-packages/sprotty-live-beans-client/package.json create mode 100644 nodejs-packages/sprotty-live-beans-client/src/app.ts create mode 100644 nodejs-packages/sprotty-live-beans-client/src/di.config.ts create mode 100644 nodejs-packages/sprotty-live-beans-client/src/model.ts create mode 100644 nodejs-packages/sprotty-live-beans-client/src/standalone.ts create mode 100644 nodejs-packages/sprotty-live-beans-client/src/views.tsx create mode 100644 nodejs-packages/sprotty-live-beans-client/tsconfig.json create mode 100644 nodejs-packages/sprotty-live-beans-client/webpack.config.js create mode 100644 vscode-extensions/vscode-spring-boot/lib/live-bean-view.ts diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/Sts4LanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/Sts4LanguageServer.java index c2dfab573..b22b798a3 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/Sts4LanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/Sts4LanguageServer.java @@ -10,7 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.languageserver; +import org.eclipse.lsp4j.jsonrpc.services.JsonDelegate; import org.eclipse.lsp4j.services.LanguageServer; +import org.springframework.ide.vscode.commons.languageserver.sprotty.SprottyService; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService; @@ -39,5 +41,9 @@ public interface Sts4LanguageServer extends LanguageServer { * @return */ DiagnosticService getDiagnosticService(); + + @JsonDelegate + SprottyService getSprottyService(); + } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/sprotty/SprottyService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/sprotty/SprottyService.java new file mode 100644 index 000000000..71ebac8e8 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/sprotty/SprottyService.java @@ -0,0 +1,12 @@ +package org.springframework.ide.vscode.commons.languageserver.sprotty; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; + +import com.google.gson.JsonObject; + +public interface SprottyService { + + @JsonNotification("sts/sprotty") + void sprottyMessage(JsonObject message); + +} diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/SprottyClient.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/SprottyClient.java new file mode 100644 index 000000000..bea4f6c7e --- /dev/null +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/SprottyClient.java @@ -0,0 +1,12 @@ +package org.springframework.ide.vscode.commons.protocol; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; + +import com.google.gson.JsonObject; + +public interface SprottyClient { + + @JsonNotification("sts/progress") + void sprottyMessage(JsonObject message); + +} diff --git a/headless-services/commons/pom.xml b/headless-services/commons/pom.xml index b4bb32e41..0b1af8663 100644 --- a/headless-services/commons/pom.xml +++ b/headless-services/commons/pom.xml @@ -42,6 +42,7 @@ commons-gradle commons-boot-app-cli language-server-starter + commons-sprotty diff --git a/headless-services/spring-boot-language-server/build.sh b/headless-services/spring-boot-language-server/build.sh index b53c273e5..b497ae374 100755 --- a/headless-services/spring-boot-language-server/build.sh +++ b/headless-services/spring-boot-language-server/build.sh @@ -8,11 +8,13 @@ if command -v xvfb-run ; then -f ../pom.xml \ -pl $modules \ -am \ - clean install + clean install \ + -DskipTests else ../mvnw \ -f ../pom.xml \ -pl $modules \ -am \ - clean install + clean install \ + -DskipTests fi diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/PopupModelFactory.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/PopupModelFactory.java index 6608b0118..5af0dc12f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/PopupModelFactory.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/PopupModelFactory.java @@ -18,18 +18,19 @@ public class PopupModelFactory implements IPopupModelFactory { @Override public SModelRoot createPopupModel(SModelElement element, RequestPopupModelAction request, IDiagramServer server) { - SGraph graph = new SGraph(); - graph.setId("popup"); - List children = new ArrayList<>(); - - SLabel label = new SLabel(); - label.setType("node:label"); - label.setText("I'm a tooltip!"); - children.add(label); - - graph.setChildren(children); - - return graph; +// SGraph graph = new SGraph(); +// graph.setId("popup"); +// List children = new ArrayList<>(); +// +// SLabel label = new SLabel(); +// label.setType("node:label"); +// label.setText("I'm a tooltip!"); +// children.add(label); +// +// graph.setChildren(children); +// +// return graph; + return null; } } diff --git a/nodejs-packages/sprotty-live-beans-client/copy-script.sh b/nodejs-packages/sprotty-live-beans-client/copy-script.sh new file mode 100644 index 000000000..20e88f15e --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/copy-script.sh @@ -0,0 +1,5 @@ +rm -rf ../../vscode-extensions/vscode-spring-boot/media/* +mkdir -p ../../vscode-extensions/vscode-spring-boot/media +cp bundle.js* ../../vscode-extensions/vscode-spring-boot/media +cp -R css ../../vscode-extensions/vscode-spring-boot/media +cp -R lib ../../vscode-extensions/vscode-spring-boot/media diff --git a/nodejs-packages/sprotty-live-beans-client/css/diagram.css b/nodejs-packages/sprotty-live-beans-client/css/diagram.css new file mode 100644 index 000000000..f92c3f853 --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/css/diagram.css @@ -0,0 +1,46 @@ +/******************************************************************************** + * Copyright (c) 2017-2018 TypeFox 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 + ********************************************************************************/ + +.sprotty-node { + fill: #aae; + stroke: #66b; + stroke-width: 3; +} + +.sprotty-text { + font-size: 16pt; + text-anchor: middle; +} + +.sprotty-edge { + fill: none; + stroke: #488; + stroke-width: 2; +} + +.sprotty-node.selected { + stroke: #dd8; + stroke-width: 6; +} + +.sprotty-missing { + stroke-width: 1; + stroke: #f00; + fill: #f00; + font-family: SansSerif; + font-size: 14pt; + text-anchor: middle; +} \ No newline at end of file diff --git a/nodejs-packages/sprotty-live-beans-client/css/page.css b/nodejs-packages/sprotty-live-beans-client/css/page.css new file mode 100644 index 000000000..fc17b4ff0 --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/css/page.css @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2017-2018 TypeFox 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 + ********************************************************************************/ + +.copyright { + margin-top: 10px; + text-align: right; + font-size: 10px; + color: #888; +} + +.help { + margin-top: 24px; + text-align: right; + font-size: 16px; + color: #888; +} + +svg { + margin-top: 15px; + width: 100%; + height: 500px; + border-style: solid; + border-width: 1px; + border-color: #bbb; +} diff --git a/nodejs-packages/sprotty-live-beans-client/index.html b/nodejs-packages/sprotty-live-beans-client/index.html new file mode 100644 index 000000000..7c171caa7 --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/index.html @@ -0,0 +1,39 @@ + + + + + + sprotty Circles Example + + + + + + +
+
+
+

sprotty Circles Example

+

+ + +

+
+
+ Help +
+
+
+
+
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/nodejs-packages/sprotty-live-beans-client/package.json b/nodejs-packages/sprotty-live-beans-client/package.json new file mode 100644 index 000000000..116e1178b --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/package.json @@ -0,0 +1,33 @@ +{ + "name": "sprotty-live-beans-client", + "version": "1.0.0", + "description": "", + "main": "bundle.js", + "scripts": { + "defunct-build": "tsc -p ./tsconfig.json", + "build": "webpack --progress --config ./webpack.config.js && bash copy-script.sh", + "run": "npx http-server -c-1 .", + "watch": "tsc -w -p ./tsconfig.json", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Kris De Volder ", + "license": "ISC", + "dependencies": { + "@types/sockjs-client": "^1.1.1", + "reflect-metadata": "^0.1.10", + "sprotty": "^0.6.0", + "sockjs-client": "^1.3.0" + }, + "devDependencies": { + "@types/node": "^12.7.2", + "circular-dependency-plugin": "^2.0.0", + "core-js": "^2.5.1", + "css-loader": "^2.1.0", + "http-server": "^0.11.1", + "style-loader": "^0.23.1", + "ts-loader": "^4.4.2", + "typescript": "^3.5.3", + "webpack": "^4.15.1", + "webpack-cli": "^3.0.8" + } +} diff --git a/nodejs-packages/sprotty-live-beans-client/src/app.ts b/nodejs-packages/sprotty-live-beans-client/src/app.ts new file mode 100644 index 000000000..b8a149a73 --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/src/app.ts @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2017-2018 TypeFox 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 "reflect-metadata"; +console.log("Loaded reflect-metadata"); + +import runStandalone from "./standalone"; + +const appDiv = document.getElementById('sprotty-app') +if(appDiv) { + const appMode = appDiv.getAttribute('data-app'); + if (appMode === 'circlegraph') + runStandalone(); + else + throw new Error('Dunno what to do :-('); +} diff --git a/nodejs-packages/sprotty-live-beans-client/src/di.config.ts b/nodejs-packages/sprotty-live-beans-client/src/di.config.ts new file mode 100644 index 000000000..9f3262d0e --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/src/di.config.ts @@ -0,0 +1,78 @@ +/******************************************************************************** + * Copyright (c) 2017-2018 TypeFox 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 {BeanNode, VSCodeWebViewDiagramServer} from "./model"; + +console.log("Loading di.config"); +import "reflect-metadata" + +import { Container, ContainerModule } from "inversify"; +import { + defaultModule, + TYPES, + configureViewerOptions, + ConsoleLogger, + LogLevel, + boundsModule, + hoverModule, + moveModule, + selectModule, + undoRedoModule, + viewportModule, + LocalModelSource, + exportModule, + CircularNode, + configureModelElement, + SGraph, + updateModule, + graphModule, + routingModule, + modelSourceModule, + SLabel, + SLabelView, + SCompartment, + SCompartmentView, SEdge +} from "sprotty"; +import {BeanNodeView, CircleNodeView, EdgeView, ExampleGraphView} from "./views"; + +export default (useWebsocket: boolean) => { + require("sprotty/css/sprotty.css"); + require("../css/diagram.css"); + const circlegraphModule = new ContainerModule((bind, unbind, isBound, rebind) => { + if (useWebsocket) + bind(TYPES.ModelSource).to(VSCodeWebViewDiagramServer).inSingletonScope(); + else + bind(TYPES.ModelSource).to(LocalModelSource).inSingletonScope(); + rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope(); + rebind(TYPES.LogLevel).toConstantValue(LogLevel.log); + const context = { bind, unbind, isBound, rebind }; + configureModelElement(context, 'graph', SGraph, ExampleGraphView); + configureModelElement(context, 'node:circle', CircularNode, CircleNodeView); + configureModelElement(context, 'node:bean', BeanNode, BeanNodeView); + configureModelElement(context, 'node:label', SLabel, SLabelView); + configureModelElement(context, 'compartment', SCompartment, SCompartmentView); + configureModelElement(context, 'edge:straight', /*OrthogonalEgde*/ SEdge, EdgeView); + configureViewerOptions(context, { + needsClientLayout: true, + needsServerLayout: true, + baseDiv: 'spring-boot' + }); + }); + + const container = new Container(); + container.load(defaultModule, selectModule, moveModule, boundsModule, undoRedoModule, viewportModule, + exportModule, updateModule, graphModule, routingModule, modelSourceModule, circlegraphModule, hoverModule); + return container; +}; diff --git a/nodejs-packages/sprotty-live-beans-client/src/model.ts b/nodejs-packages/sprotty-live-beans-client/src/model.ts new file mode 100644 index 000000000..0a26dae59 --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/src/model.ts @@ -0,0 +1,67 @@ +import {ManhattanEdgeRouter, RectangularNode, SEdge, WebSocketDiagramServer, ActionMessage, DiagramServer} from "sprotty"; +import {injectable} from 'inversify'; + +export class BeanNode extends RectangularNode { + +} + +export class OrthogonalEgde extends SEdge { + + routerKind = ManhattanEdgeRouter.KIND; + +} + +const SOCKET_MESSAGE_BUFFER = 4000; +const END_MESSAGE = '@end'; + +@injectable() +export class ExampleWebsocketDiagramServer extends WebSocketDiagramServer { + + protected sendMessage(message: ActionMessage): void { + if (this.webSocket) { + const messageStr = JSON.stringify(message); + let offset = 0; + while (offset < messageStr.length) { + this.webSocket.send(messageStr.substring(offset, offset + SOCKET_MESSAGE_BUFFER)); + offset += SOCKET_MESSAGE_BUFFER; + } + this.webSocket.send(END_MESSAGE); + } else { + throw new Error('WebSocket is not connected'); + } + } +} + +@injectable() +export class VSCodeWebViewDiagramServer extends DiagramServer { + + protected vscode: any; + + listen(vscode: any): void { + + window.addEventListener('message', event => { + const message = event.data; // The json data that the extension sent + console.log('Message Received'); + this.messageReceived(message); + }); + + this.vscode = vscode; + + } + + // disconnect() { + // if (this.webSocket) { + // this.webSocket.close(); + // this.webSocket = undefined; + // } + // } + // + protected sendMessage(message: ActionMessage): void { + console.log('Send Message'); + if (this.vscode) { + this.vscode.postMessage(message); + } else { + throw new Error('No VSCode api object'); + } + } +} diff --git a/nodejs-packages/sprotty-live-beans-client/src/standalone.ts b/nodejs-packages/sprotty-live-beans-client/src/standalone.ts new file mode 100644 index 000000000..a7c38da59 --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/src/standalone.ts @@ -0,0 +1,118 @@ +/******************************************************************************** + * Copyright (c) 2017-2018 TypeFox 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 + ********************************************************************************/ + +declare const acquireVsCodeApi: any; + +import { + TYPES, IActionDispatcher, SModelElementSchema, SEdgeSchema, SNodeSchema, SGraphSchema, + ModelSource, LocalModelSource, WebSocketDiagramServer, RequestModelAction +} from "sprotty"; +import createContainer from "./di.config"; +import * as SockJS from "sockjs-client"; +import {VSCodeWebViewDiagramServer} from "./model"; + +export default function runStandalone() { + const container = createContainer(true); + const dispatcher = container.get(TYPES.IActionDispatcher); + + // Initialize gmodel + const node0 = { + id: 'node0', type: 'node:bean', position: {x: 100, y: 100}, size: {width: 80, height: 80}, + layout: 'vbox', + + children: [ + { + id: 'node0_header', + type: 'compartment', + layout: 'hbox', + children: [ + { + id: 'bean-name', + type: 'node:label', + text: 'SpringBootApplication' + } + ] + } + ] + }; + + // const children_for_node = []; + + + const graph: SGraphSchema = {id: 'graph', type: 'graph', children: [node0]}; + + let count = 2; + + function addNode(): SModelElementSchema[] { + const newNode: SNodeSchema = { + id: 'node' + count, + type: 'node:bean', + position: { + x: Math.random() * 1024, + y: Math.random() * 768 + }, + size: { + width: 80, + height: 80 + } + }; + const newEdge: SEdgeSchema = { + id: 'edge' + count, + type: 'edge:straight', + sourceId: 'node0', + targetId: 'node' + count++ + }; + return [newNode, newEdge]; + } + + for (let i = 0; i < 10; ++i) { + const newElements = addNode(); + for (const e of newElements) { + graph.children.splice(0, 0, e); + } + } + + // Run + const modelSource = container.get(TYPES.ModelSource); + console.log('Model source: ' + modelSource); + if (modelSource instanceof LocalModelSource) { + (modelSource).setModel(graph); + } + + if (modelSource instanceof WebSocketDiagramServer) { + const ws = new SockJS('http://localhost:8080/websocket'); + modelSource.clientId = 'spring-boot'; + modelSource.listen(ws); + ws.addEventListener('open', () => { + dispatcher.dispatch(new RequestModelAction()); + }); + ws.addEventListener('error', (event) => { + console.error(`WebSocket Error: ${event}`) + }) + } + + if (modelSource instanceof VSCodeWebViewDiagramServer) { + modelSource.listen(acquireVsCodeApi()); + } + + console.log('Before requesting model'); + + // Button features + document.getElementById('refresh')!.addEventListener('click', () => { + dispatcher.dispatch(new RequestModelAction()); + }); + +} diff --git a/nodejs-packages/sprotty-live-beans-client/src/views.tsx b/nodejs-packages/sprotty-live-beans-client/src/views.tsx new file mode 100644 index 000000000..010efdd1b --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/src/views.tsx @@ -0,0 +1,107 @@ +/******************************************************************************** + * Copyright (c) 2017-2018 TypeFox 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 + ********************************************************************************/ + +/** @jsx svg */ +import { svg } from 'snabbdom-jsx'; + +import { VNode } from "snabbdom/vnode"; +import { + RenderingContext, + SNode, + IView, + SGraphView, + SGraph, + PolylineEdgeView, + SEdge, + Point, + RectangularNodeView, + Selectable, + SPort, + Hoverable, + SShapeElement +} from "sprotty"; +import { injectable } from 'inversify'; + +/** + * A very simple example node consisting of a plain circle. + */ +@injectable() +export class CircleNodeView implements IView { + render(node: SNode, context: RenderingContext): VNode { + const radius = this.getRadius(node); + return + + {node.id.substr(4)} + ; + } + + protected getRadius(node: SNode) { + return 40; + } +} + +@injectable() +export class ExampleGraphView extends SGraphView { + + render(model: Readonly, context: RenderingContext): VNode { + const transform = `scale(${model.zoom}) translate(${-model.scroll.x},${-model.scroll.y})`; + return + + + + + + + + + + + {context.renderChildren(model)} + + ; + } + +} + +export class EdgeView extends PolylineEdgeView { + + protected renderLine(edge: SEdge, segments: Point[], context: RenderingContext): VNode { + const firstPoint = segments[0]; + let path = `M ${firstPoint.x},${firstPoint.y}`; + for (let i = 1; i < segments.length; i++) { + const p = segments[i]; + path += ` L ${p.x},${p.y}`; + } + const dom: any = ; + dom.data.attrs['marker-end'] = 'url(#arrow)'; + return dom; + } + +} + +@injectable() +export class BeanNodeView extends RectangularNodeView { + render(node: Readonly, context: RenderingContext): VNode { + return + + {context.renderChildren(node)} + ; + } +} + diff --git a/nodejs-packages/sprotty-live-beans-client/tsconfig.json b/nodejs-packages/sprotty-live-beans-client/tsconfig.json new file mode 100644 index 000000000..3cafe4128 --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "lib": [ + "es6", "dom" + ], + "types": [ + "node", "reflect-metadata", + ], + "typeRoots": [ + "node_modules/@types", "typings" + ], + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "strictNullChecks": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "outDir": "lib", + "jsx": "react" + }, + "include": [ + "src", "typings" + ], + "exclude": [ + "**/*.spec.ts", "**/*.spec.tsx" + ] + } \ No newline at end of file diff --git a/nodejs-packages/sprotty-live-beans-client/webpack.config.js b/nodejs-packages/sprotty-live-beans-client/webpack.config.js new file mode 100644 index 000000000..820c93e98 --- /dev/null +++ b/nodejs-packages/sprotty-live-beans-client/webpack.config.js @@ -0,0 +1,53 @@ +var webpack = require('webpack'); +var path = require('path'); +var CircularDependencyPlugin = require('circular-dependency-plugin'); + +module.exports = { + entry: [ + 'core-js/es6/map', + 'core-js/es6/promise', + 'core-js/es6/string', + 'core-js/es6/symbol', + './src/app.ts' + ], + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, '.') + }, + mode: 'development', + devtool: 'source-map', + resolve: { + // Add `.ts` and `.tsx` as a resolvable extension. + extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'] + }, + module: { + rules: [ + // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader` + { + test: /\.tsx?$/, + use: [{ + loader: 'ts-loader', + options: { + configFile: path.resolve(__dirname, 'tsconfig.json') + } + }] + }, + { + test: /\.css$/, + exclude: /\.useable\.css$/, + loader: 'style-loader!css-loader' + } + ] + }, + node : { fs: 'empty', net: 'empty' }, + plugins: [ + new CircularDependencyPlugin({ + exclude: /(node_modules|examples)\/./, + failOnError: false + }), + new webpack.WatchIgnorePlugin([ + /\.js$/, + /\.d\.ts$/ + ]) + ] +}; diff --git a/vscode-extensions/vscode-spring-boot/.gitignore b/vscode-extensions/vscode-spring-boot/.gitignore index 37494b06d..c9b592ae1 100644 --- a/vscode-extensions/vscode-spring-boot/.gitignore +++ b/vscode-extensions/vscode-spring-boot/.gitignore @@ -15,4 +15,5 @@ src/test/resources/test-projects/**/.classpath src/test/resources/test-projects/**/.project src/test/resources/test-projects/**/.factorypath src/test/resources/test-projects/**/.settings/** -src/test/resources/test-projects/**/bin/** \ No newline at end of file +src/test/resources/test-projects/**/bin/** +/media/ diff --git a/vscode-extensions/vscode-spring-boot/lib/Main.ts b/vscode-extensions/vscode-spring-boot/lib/Main.ts index ff83022ec..2cdc79f67 100644 --- a/vscode-extensions/vscode-spring-boot/lib/Main.ts +++ b/vscode-extensions/vscode-spring-boot/lib/Main.ts @@ -2,10 +2,11 @@ import * as VSCode from 'vscode'; import {workspace} from 'vscode'; +import * as LiveBeansModule from './live-bean-view'; import * as commons from '@pivotal-tools/commons-vscode'; -import {LanguageClient} from "vscode-languageclient"; +import {LanguageClient, NotificationType} from "vscode-languageclient"; const PROPERTIES_LANGUAGE_ID = "spring-boot-properties"; const YAML_LANGUAGE_ID = "spring-boot-properties-yaml"; @@ -18,7 +19,7 @@ export function activate(context: VSCode.ExtensionContext): Thenable { @@ -67,5 +68,7 @@ export function activate(context: VSCode.ExtensionContext): Thenable { + return LiveBeansModule.activate(context, client).then(() => client); + }) } diff --git a/vscode-extensions/vscode-spring-boot/lib/live-bean-view.ts b/vscode-extensions/vscode-spring-boot/lib/live-bean-view.ts new file mode 100644 index 000000000..395973bcb --- /dev/null +++ b/vscode-extensions/vscode-spring-boot/lib/live-bean-view.ts @@ -0,0 +1,255 @@ +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as SockJS from 'sockjs-client'; +import * as WebSocket from 'ws'; +import {LanguageClient, NotificationType} from "vscode-languageclient"; + +export async function activate(context: vscode.ExtensionContext, client: LanguageClient) { + + context.subscriptions.push( + vscode.commands.registerCommand('sts4.liveBeans.start', () => { + LiveBeansView.createOrShow(context.extensionPath, client); + }) + ); + +} + +/** + * Manages cat coding webview panels + */ +class LiveBeansView { + /** + * Track the currently panel. Only allow a single panel to exist at a time. + */ + public static currentPanel: LiveBeansView | undefined; + + public static readonly viewType = 'catCoding'; + + private readonly _panel: vscode.WebviewPanel; + private readonly _extensionPath: string; + private _disposables: vscode.Disposable[] = []; + + public static createOrShow(extensionPath: string, client: LanguageClient) { + const column = vscode.window.activeTextEditor + ? vscode.window.activeTextEditor.viewColumn + : undefined; + + // If we already have a panel, show it. + if (LiveBeansView.currentPanel) { + LiveBeansView.currentPanel._panel.reveal(column); + return; + } + + // Otherwise, create a new panel. + const panel = vscode.window.createWebviewPanel( + LiveBeansView.viewType, + 'Live Beans', + column || vscode.ViewColumn.One, + { + // Enable javascript in the webview + enableScripts: true, + + + + // And restrict the webview to only loading content from our extension's `media` directory. + localResourceRoots: [vscode.Uri.file(path.join(extensionPath, 'media'))] + } + ); + + LiveBeansView.currentPanel = new LiveBeansView(panel, extensionPath); + + console.log('Created webview panel!'); + const bridge: LSWebViewToLSPBridge = new LSWebViewToLSPBridge(panel.webview, client); + panel.webview.onDidReceiveMessage(message => bridge.sendToLs(message)); + } + + public static revive(panel: vscode.WebviewPanel, extensionPath: string) { + LiveBeansView.currentPanel = new LiveBeansView(panel, extensionPath); + } + + private constructor(panel: vscode.WebviewPanel, extensionPath: string) { + this._panel = panel; + this._extensionPath = extensionPath; + + // Set the webview's initial html content + this._update(); + + // Listen for when the panel is disposed + // This happens when the user closes the panel or when the panel is closed programatically + this._panel.onDidDispose(() => this.dispose(), null, this._disposables); + + // Update the content based on view changes + this._panel.onDidChangeViewState( + e => { + if (this._panel.visible) { + this._update(); + } + }, + null, + this._disposables + ); + + // Handle messages from the webview + this._panel.webview.onDidReceiveMessage( + message => { + switch (message.command) { + case 'alert': + vscode.window.showErrorMessage(message.text); + return; + } + }, + null, + this._disposables + ); + } + + public doRefactor() { + // Send a message to the webview webview. + // You can send any JSON serializable data. + this._panel.webview.postMessage({ command: 'refactor' }); + } + + public dispose() { + LiveBeansView.currentPanel = undefined; + + // Clean up our resources + this._panel.dispose(); + + while (this._disposables.length) { + const x = this._disposables.pop(); + if (x) { + x.dispose(); + } + } + } + + private _update() { + this._panel.webview.html = this._getHtmlForWebview(/*cats[catName]*/); + } + + mediaUrl(...pathsegments: string[]) { + const mediaPath = path.join(this._extensionPath, 'media') + + // Local path to main script run in the webview + const scriptPathOnDisk = vscode.Uri.file( + path.join(mediaPath, ...pathsegments) + ); + + // And the uri we use to load this script in the webview + return scriptPathOnDisk.with({ scheme: 'vscode-resource' }); + } + + private _getHtmlForWebview(/*catGif: string*/) { + + // And the uri we use to load this script in the webview + const scriptUri = this.mediaUrl('bundle.js'); + const cssUri = this.mediaUrl('css', 'page.css'); + + // Use a nonce to whitelist which scripts can be run + const nonce = getNonce(); + + return ` + + + + + sprotty Circles Example + + + + + + +
+
+
+

sprotty Circles Example

+

+ + +

+
+
+ Help +
+
+
+
+
+
+ +
+
+ + + `; + } +} + +function getNonce() { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} + +const SOCKET_MESSAGE_BUFFER = 4000; +const END_MESSAGE = '@end'; + +interface LSWebviewBridgeConstructor { + create(webview: vscode.Webview): LSWebViewBridge; +} + +interface LSWebViewBridge { + sendToLs(message: any); +} + +class LSWebViewToWebsocketBridge implements LSWebViewBridge { + private ws: WebSocket; + + constructor(webview: vscode.Webview): LSWebViewBridge { + this.ws = new SockJS('http://localhost:8080/websocket'); + this.ws.addEventListener('message', (event) => { + console.dir(event.data); + const actionMessage = JSON.parse(event.data); + webview.postMessage(actionMessage); + }); + } + + sendToLs(message: any) { + console.log('Send messge Webview -> LS'); + const messageStr = JSON.stringify(message); + for (let offset = 0; offset < messageStr.length; offset += SOCKET_MESSAGE_BUFFER) { + this.ws.send(messageStr.substring(offset, offset + SOCKET_MESSAGE_BUFFER)); + } + this.ws.send(END_MESSAGE); + } + +} + +const SPROTTY_LSP_NOTIFICATION = new NotificationType("sts/sprotty"); + +class LSWebViewToLSPBridge implements LSWebViewBridge { + + private client: LanguageClient; + + constructor(webview: vscode.Webview, client: LanguageClient) { + client.onReady().then(() => { + client.onNotification(SPROTTY_LSP_NOTIFICATION, async (params: any) => + webview.postMessage(params) + ); + }); + this.client = client; + + } + + sendToLs(message: any) { + this.client.onReady().then(() => { + this.client.sendNotification(SPROTTY_LSP_NOTIFICATION, message); + }); + } +} diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 08fac08b1..659371f31 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -29,7 +29,8 @@ "onLanguage:spring-boot-properties", "onLanguage:spring-boot-properties-yaml", "onLanguage:java", - "onLanguage:xml" + "onLanguage:xml", + "onCommand:sts4.liveBeans.start" ], "contributes": { "javaExtensions": [ @@ -176,6 +177,13 @@ "scopeName": "source.java-properties", "path": "./properties-support/java-properties.tmLanguage" } + ], + "commands": [ + { + "command": "sts4.liveBeans.start", + "title": "Live Beans Graph", + "category": "Spring-Boot" + } ] }, "main": "./out/lib/Main", @@ -190,12 +198,15 @@ }, "dependencies": { "@pivotal-tools/commons-vscode": "file:../commons-vscode/pivotal-tools-commons-vscode-0.2.2.tgz", + "sockjs-client": "1.3.0", "vscode-languageclient": "5.2.1" }, "devDependencies": { - "vsce": "^1.36.1", + "@types/sockjs-client": "^1.1.1", + "@types/node": "^9.6.51", + "@types/ws": "^6.0.2", "typescript": "2.7.2", - "@types/node": "^9.4.6", + "vsce": "^1.36.1", "vscode": "^1.1.33" }, "extensionDependencies": [ diff --git a/vscode-extensions/vscode-spring-boot/tsconfig.json b/vscode-extensions/vscode-spring-boot/tsconfig.json index 1905ffea7..0a6eef400 100644 --- a/vscode-extensions/vscode-spring-boot/tsconfig.json +++ b/vscode-extensions/vscode-spring-boot/tsconfig.json @@ -9,7 +9,8 @@ "declaration": true, "outDir": "out", "sourceMap": true, - "rootDir": "." + "rootDir": ".", + "skipLibCheck": true }, "include": [ "typings/*.d.ts",