Skip to content

Commit

Permalink
Merge pull request #16 from SoftVarE-Group/dev
Browse files Browse the repository at this point in the history
Dev into master
  • Loading branch information
st-vi authored Dec 5, 2023
2 parents 43995a8 + 15512ff commit b4091a1
Show file tree
Hide file tree
Showing 15 changed files with 301 additions and 225 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/playground_deployment_dev.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Deploy UVL Playground
name: Deploy UVL Playground Dev

env:
HOSTNAME: 7470a63e-7b47-4833-a06c-1513f4fc534d.ul.bw-cloud-instance.org
Expand All @@ -22,9 +22,12 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
ref: dev
- run: git branch -a
- run: cat ./WebSocketClient/index.html

- name: Set hostname for WebSocketLanguageServer
run: |
sed -i "s/languageServerHostName: .*/languageServerHostName: \"$HOSTNAME\",/" ./WebSocketClient/src/config.ts
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,7 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*


.vscode/
.idea/
39 changes: 24 additions & 15 deletions WebSocketClient/index.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css">
<title>UVL Playground</title>
</head>
<body>
<div class="container">
<h1>UVL Playground</h1>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="style.css">
<title>UVL Playground</title>
</head>
<body>
<div class="header">
<h1>UVL Playground</h1>
<h2 id="connection" style="color: red;"></h2>
</div>
<div id="flex-container" class="flex-container">
<div id="container" class="editor"></div>
<p>The Universal Variability Language (UVL) is a community effort towards a widely adopted textual specification for feature models. This playground provides the opportunity to get used to the language with syntax-highlighting, autocompletion, and simple analysis. It is based on the <a href="https://github.com/Universal-Variability-Language/uvl-lsp">UVL Language Server</a>. To fully use all features use the UVLS - Universal Variability Language Server extension for visual studio code. </p>
<div class="graph"></div>
</div>

<div class="footer">
<p>The Universal Variability Language (UVL) is a community effort towards a widely adopted textual specification
for feature models. This playground provides the opportunity to get used to the language with
syntax-highlighting, autocompletion, and simple analysis. It is based on the <a
href="https://github.com/Universal-Variability-Language/uvl-lsp">UVL Language Server</a>. To fully
use all features use the UVLS - Universal Variability Language Server extension for visual studio code. </p>
</div>



<script type="module">
import { startPythonClient } from "./src/main.ts";
import {startPythonClient} from "./src/main.ts";

startPythonClient();
</script>
</body>
</body>
</html>
32 changes: 32 additions & 0 deletions WebSocketClient/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions WebSocketClient/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"devDependencies": {
"@types/node": "^20.8.7",
"@types/uuid": "^9.0.7",
"@types/vscode": "~1.83.0",
"typescript": "^5.0.2",
"vite": "^4.5.0",
Expand All @@ -25,10 +26,13 @@
"@codingame/monaco-vscode-textmate-service-override": "~1.83.2",
"@codingame/monaco-vscode-theme-defaults-default-extension": "~1.83.2",
"@codingame/monaco-vscode-theme-service-override": "~1.83.2",
"@viz-js/viz": "^3.2.3",
"dotenv": "^16.3.1",
"lodash": "^4.17.21",
"monaco-editor": "^0.44.0",
"monaco-editor-workers": "~0.44.0",
"monaco-languageclient": "^6.6.0",
"uuid": "^9.0.1",
"vite": "~4.4.11",
"vscode-json-languageservice": "~5.3.7",
"vscode-uri": "~3.0.8",
Expand Down
102 changes: 97 additions & 5 deletions WebSocketClient/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@ import { WebSocketMessageReader, WebSocketMessageWriter, toSocket } from 'vscode
import { RegisteredFileSystemProvider, registerFileSystemOverlay, RegisteredMemoryFile } from 'vscode/service-override/files';
import {LogLevel, Uri} from 'vscode';
import config from './config.js';
import { instance } from "@viz-js/viz";
import { Message } from 'vscode-jsonrpc';
import { v4 as uuidv4 } from 'uuid';
import lodash from 'lodash';
import { ExecuteCommandRequest } from 'vscode-languageserver-protocol'

import { buildWorkerDefinition } from 'monaco-editor-workers';
buildWorkerDefinition('./node_modules/monaco-editor-workers/dist/workers', new URL('', window.location.href).href, false);

const languageId = 'uvls';
let languageClient: MonacoLanguageClient;
let fileID;
let model;
const connectionText = document.getElementById("connection");

const createUrl = (hostname: string, port: number, path: string, searchParams: Record<string, any> = {}, secure: boolean): string => {
const protocol = secure ? 'wss' : 'ws';
Expand All @@ -43,7 +51,18 @@ const createUrl = (hostname: string, port: number, path: string, searchParams: R

const createWebSocket = (url: string): WebSocket => {
const webSocket = new WebSocket(url);
webSocket.onerror = () => {
if(connectionText){
connectionText.textContent = "Could not connect to language server. Reconnecting ...";
}
setTimeout(() => {
createWebSocket(url);
}, 1000);
};
webSocket.onopen = async () => {
if(connectionText){
connectionText.textContent = "";
}
const socket = toSocket(webSocket);
const reader = new WebSocketMessageReader(socket);
const writer = new WebSocketMessageWriter(socket);
Expand All @@ -52,13 +71,16 @@ const createWebSocket = (url: string): WebSocket => {
writer
});
await languageClient.start();
reader.onClose(() => languageClient.stop());
reader.onClose(() => {
languageClient.stop();
createWebSocket(url);
});
};
return webSocket;
};

const createLanguageClient = (transports: MessageTransports): MonacoLanguageClient => {
return new MonacoLanguageClient({
const client = new MonacoLanguageClient({
name: 'UVL Language Client',
clientOptions: {
// use a language id as a document selector
Expand All @@ -76,6 +98,42 @@ const createLanguageClient = (transports: MessageTransports): MonacoLanguageClie
},
synchronize: {
fileEvents: [vscode.workspace.createFileSystemWatcher('**')]
},
connectionOptions: {
// This construct can be used to filter the messages we are receiving from the language server
messageStrategy: {
handleMessage(message: Message, next: (message: Message) => void) {
if(Message.isRequest(message)){
// Filters requests send by uvls -> Anti-Pattern in our opinion
}
else if(Message.isResponse(message)){
// Filters responses send by uvls
}
else if(Message.isNotification(message)){
// Filters Notification messages following json-rpc spec
}
// "next" is the default behaviour
next(message);
}
}
},
// The Middleware allows us to intercept all messages that would be sent to the language server
middleware: {
executeCommand(command, args, next) {
const information = {command: command, arguments: args};
if(command === "uvls/open_config") {
//we do not support config view
return;
}
else if(command === "uvls/generate_diagram") {
client?.sendRequest(ExecuteCommandRequest.type, information).then((res) => {
createDiagramFromDot(res as string);
});
}
else {
next(command, args);
}
},
}
},
// create a language client connection from the JSON RPC connection on demand
Expand All @@ -85,8 +143,16 @@ const createLanguageClient = (transports: MessageTransports): MonacoLanguageClie
}
}
});
return client;
};

function createDiagramFromDot(res: string): void {
instance().then(viz => {
const div = document.getElementsByClassName("graph");
div[0].replaceChildren(viz.renderSVGElement(res!));
});
}

export const startPythonClient = async () => {
// init vscode-api
const useDebugLogging = config.debug ? LogLevel.Debug : LogLevel.Off;
Expand All @@ -95,7 +161,7 @@ export const startPythonClient = async () => {
...getThemeServiceOverride(),
...getTextmateServiceOverride(),
...getConfigurationServiceOverride(Uri.file('/workspace')),
...getKeybindingsServiceOverride()
...getKeybindingsServiceOverride(),
},
debugLogging: config.debug,
logLevel: useDebugLogging,
Expand Down Expand Up @@ -133,7 +199,8 @@ export const startPythonClient = async () => {
}`);

const fileSystemProvider = new RegisteredFileSystemProvider(false);
fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/workspace/fm.uvl'), 'features\n\tfeature1\n\nconstraints\n\tfeature1'));
fileID = uuidv4();
fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file(`/workspace/${fileID}.uvl`), getInitialFm()));
registerFileSystemOverlay(1, fileSystemProvider);

// create the web socket and configure to start the language client on open, can add extra parameters to the url if needed.
Expand All @@ -145,8 +212,17 @@ export const startPythonClient = async () => {
}, location.protocol === 'https:'));



// use the file create before
const modelRef = await createModelReference(monaco.Uri.file('/workspace/fm.uvl'));
const modelRef = await createModelReference(monaco.Uri.file(`/workspace/${fileID}.uvl`));
model = modelRef.object;

const debouncedSave = lodash.debounce(saveFm, 1000);
modelRef.object.onDidChangeContent(() => {
debouncedSave();
});


modelRef.object.setLanguageId(languageId);

// create monaco editor
Expand All @@ -155,3 +231,19 @@ export const startPythonClient = async () => {
automaticLayout: true
});
};

function getInitialFm(){
let initialFm = "features\n\tfeature1\n\t\tor\n\t\t\tfeature2\n\t\t\tfeature3\n\nconstraints\n\tfeature1";
const storedFm = window.localStorage.getItem("fm");
if(storedFm !== null){
initialFm = storedFm;
}
return initialFm;
}

function saveFm(){
if(model !== undefined){
const content = model.textEditorModel?.getValue();
window.localStorage.setItem("fm", content);
}
}
Loading

0 comments on commit b4091a1

Please sign in to comment.