diff --git a/.gitignore b/.gitignore index 4e80219..22cfdea 100644 --- a/.gitignore +++ b/.gitignore @@ -27,5 +27,6 @@ **/mvnw.cmd **/.yarn **/.yarnrc.yml +**/*.jar /extension/*.cjs /extension/*.mjs \ No newline at end of file diff --git a/c4-server/pom.xml b/c4-server/pom.xml index 6618c43..546ad38 100644 --- a/c4-server/pom.xml +++ b/c4-server/pom.xml @@ -1,191 +1,216 @@ - - 4.0.0 - - ru.beeatlas.c4 - c4-server - 1.0 - - c4-server - http://www.beeatlas.ru - - - UTF-8 - 17 - 1.9.24 - 4.1.0 - - - - - - org.apache.commons - commons-text - 1.14.0 - - - - ch.qos.logback - logback-classic - 1.5.20 - compile - - - - info.picocli - picocli - 4.7.7 - - - - com.google.inject - guice - 7.0.0 - - - - org.eclipse.lsp4j - org.eclipse.lsp4j - 0.24.0 - - - - com.structurizr - structurizr-dsl - ${structurizr.version} - - - - com.structurizr - structurizr-export - ${structurizr.version} - - - - org.aspectj - aspectjrt - ${aspectj.version} - - - - org.aspectj - aspectjweaver - ${aspectj.version} - - - junit - junit - 4.11 - test - - - - org.mockito - mockito-core - 5.18.0 - test - - - - org.junit.jupiter - junit-jupiter - 5.13.1 - test - - - - org.assertj - assertj-core - 3.27.3 - test - - - - - ${project.artifactId} - - - org.apache.maven.plugins - maven-compiler-plugin - 3.15.0 - - false - - - - maven-jar-plugin - 3.4.2 - - - - - - true - lib/ - - ru.beeatlas.c4.C4LanguageServerLauncher - - - - - - org.codehaus.mojo - aspectj-maven-plugin - 1.15.0 - - + 4.0.0 + ru.beeatlas.c4 + c4-server + 1.0 + c4-server + http://www.beeatlas.ru + + UTF-8 + 17 + 1.9.24 + 4.1.0 + + + + + org.apache.commons + commons-text + 1.14.0 + + + + ch.qos.logback + logback-classic + 1.5.20 + compile + + + + com.google.inject + guice + 7.0.0 + + + + org.eclipse.lsp4j + org.eclipse.lsp4j + 1.0.0 + + + + com.structurizr + structurizr-dsl + ${structurizr.version} + + + + com.structurizr + structurizr-export + ${structurizr.version} + + + org.aspectj - aspectjtools + aspectjrt ${aspectj.version} - - - - 17 - true - true - ignore - ${project.build.sourceEncoding} - - - com.structurizr - structurizr-dsl - - - com.structurizr - structurizr-client - - - - - - - compile - - - - - - maven-assembly-plugin - 3.7.1 - - server - ${outputDirectory} - false - - src/assembly/assembly.xml - - - - - package - - single - - - - - - + + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + junit + junit + 4.11 + test + + + + org.mockito + mockito-core + 5.18.0 + test + + + + org.junit.jupiter + junit-jupiter + 5.13.1 + test + + + + org.assertj + assertj-core + 3.27.3 + test + + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + false + + + + org.codehaus.mojo + aspectj-maven-plugin + 1.16.0 + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + 17 + true + true + ignore + ${project.build.sourceEncoding} + + + com.structurizr + structurizr-dsl + + + com.structurizr + structurizr-client + + + + + + + compile + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.1 + + + package + + shade + + + ${outputDirectory} + server + false + true + false + + + junit:junit + org.junit.jupiter:* + org.mockito:* + org.assertj:* + org.hamcrest:* + org.checkerframework:checker-qual + com.google.errorprone:error_prone_annotations + com.google.j2objc:j2objc-annotations + com.google.code.findbugs:jsr305 + javax.activation:javax.activation-api + javax.xml.bind:jaxb-api + com.structurizr:structurizr-dsl + com.structurizr:structurizr-client + org.aspectj:aspectjrt + com.google.guava:listenablefuture + + + + + commons-logging:commons-logging + ** + + META-INF/versions/** + org/apache/commons/logging/impl/Jdk13* + org/apache/commons/logging/impl/Jdk14* + org/apache/commons/logging/impl/Avalon* + org/apache/commons/logging/impl/LogKit* + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/MANIFEST.MF* + META-INF/DEPENDENCIES + META-INF/*LICENSE* + META-INF/*NOTICE* + module-info.class + META-INF/versions/*/module-info.class + about.html + + + + + + ru.beeatlas.c4.C4LanguageServerLauncher + + + + + + + + + \ No newline at end of file diff --git a/c4-server/src/assembly/assembly.xml b/c4-server/src/assembly/assembly.xml deleted file mode 100644 index 234953f..0000000 --- a/c4-server/src/assembly/assembly.xml +++ /dev/null @@ -1,32 +0,0 @@ - - bin - - dir - - false - - - - javax.activation:* - javax.xml.bind:jaxb-api - com.google.code.findbugs:jsr305 - com.structurizr:structurizr-dsl - com.structurizr:structurizr-client - com.structurizr:structurizr-import - - false - lib - false - - - - - - ${project.build.directory} - - - *.jar - - - - \ No newline at end of file diff --git a/c4-server/src/main/java/ru/beeatlas/c4/C4LanguageServerLauncher.java b/c4-server/src/main/java/ru/beeatlas/c4/C4LanguageServerLauncher.java index 446f546..95f712c 100644 --- a/c4-server/src/main/java/ru/beeatlas/c4/C4LanguageServerLauncher.java +++ b/c4-server/src/main/java/ru/beeatlas/c4/C4LanguageServerLauncher.java @@ -16,75 +16,38 @@ package ru.beeatlas.c4; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.StandardSocketOptions; -import java.nio.channels.Channels; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.nio.channels.AsynchronousSocketChannel; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.launch.LSPLauncher; import org.eclipse.lsp4j.services.LanguageClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import ru.beeatlas.c4.custom.Custom; import ru.beeatlas.c4.service.C4LanguageServer; -import picocli.CommandLine; -import picocli.CommandLine.Option; - -public class C4LanguageServerLauncher implements Callable { - - private static final Logger logger = LoggerFactory.getLogger(C4LanguageServerLauncher.class); - - @Option(names = {"-e", "--echo"}, description = "Echo to the client, to inform that socket can now accept incoming connections") - private String echo = "READY_TO_CONNECT"; - - @Override - public Integer call() throws Exception { - - try { - - final AsynchronousServerSocketChannel serverSocket = AsynchronousServerSocketChannel.open(); - if (serverSocket.isOpen()) { - serverSocket.setOption(StandardSocketOptions.SO_RCVBUF, 1024); - serverSocket.setOption(StandardSocketOptions.SO_REUSEADDR, true); - serverSocket.bind(new InetSocketAddress("127.0.0.1", 5008)); - Future acceptFuture = serverSocket.accept(); - // echo to the client, that server is ready to receive incoming connections - System.out.println(echo); - final AsynchronousSocketChannel socketChannel = acceptFuture.get(); - serverSocket.close(); - InputStream in = Channels.newInputStream(socketChannel); - OutputStream out = Channels.newOutputStream(socketChannel); - C4LanguageServer c4LanguageServer = new C4LanguageServer(); - Launcher launcher = LSPLauncher.createServerLauncher(c4LanguageServer, in, out); - // Get the client that request to launch the LS. - LanguageClient client = launcher.getRemoteProxy(); - // Set the client to language server - c4LanguageServer.connect(client); - Custom.getInstance().setClient(client); - // Start the listener for JsonRPC - Future startListening = launcher.startListening(); - // Get the computed result from LS. - startListening.get(); - } - } catch (ExecutionException | InterruptedException | IOException e) { - logger.error(e.getMessage()); - Thread.currentThread().interrupt(); - } - return 1; - } +public class C4LanguageServerLauncher { public static void main(String[] args) { - int exitCode = new CommandLine( new C4LanguageServerLauncher()).execute(args); - System.exit(exitCode); + InputStream in = System.in; + OutputStream out = System.out; + System.setOut(System.err); + C4LanguageServer c4LanguageServer = new C4LanguageServer(); + Launcher launcher = LSPLauncher.createServerLauncher(c4LanguageServer, in, out); + // Get the client that request to launch the LS. + LanguageClient client = launcher.getRemoteProxy(); + // Set the client to language server + c4LanguageServer.connect(client); + Custom.getInstance().setClient(client); + // Start the listener for JsonRPC + Future startListening = launcher.startListening(); + // Get the computed result from LS. + try { + startListening.get(); + } catch (InterruptedException | ExecutionException e) { + System.exit(1); + } + System.exit(0); } } diff --git a/c4-server/src/main/java/ru/beeatlas/c4/custom/Custom.java b/c4-server/src/main/java/ru/beeatlas/c4/custom/Custom.java index cf657d6..556c5c8 100644 --- a/c4-server/src/main/java/ru/beeatlas/c4/custom/Custom.java +++ b/c4-server/src/main/java/ru/beeatlas/c4/custom/Custom.java @@ -44,6 +44,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -78,6 +79,7 @@ import ru.beeatlas.c4.dto.CodeLensCommandArgs; import ru.beeatlas.c4.model.C4DocumentModel; import ru.beeatlas.c4.model.C4ObjectWithContext; +import ru.beeatlas.c4.model.DecoratorRange; import ru.beeatlas.c4.model.C4DocumentModel.C4CompletionScope; import ru.beeatlas.c4.utils.LineToken; import ru.beeatlas.c4.utils.LineTokenizer; @@ -126,6 +128,7 @@ private boolean isValidURL(String url) { new URL(url).toURI(); return true; } catch (Exception e) { + logger.debug(e.getMessage()); return false; } } @@ -278,6 +281,7 @@ public void checkServerTrusted(X509Certificate[] certs, String authType) { allTrustingTrustManager = sc.getSocketFactory(); allTrustingHostnameVerifier = (String hostname, SSLSession session) -> true; } catch (Exception e) { + logger.debug(e.getMessage()); } } @@ -296,7 +300,7 @@ private void updateTechCapabilities() { URLConnection conn = archOpsApiConnection("GET", path, null, null); try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { techCapabilities.set(Arrays.stream((new Gson()).fromJson(in, TechCapability[].class)) - .collect(Collectors.toMap(TechCapability::code, Function.identity()))); + .collect(Collectors.toMap(cap -> cap.code().toLowerCase(), Function.identity()))); } catch(IOException e) { throw e; } @@ -330,7 +334,7 @@ private void updateCapabilities() { URLConnection conn = archOpsApiConnection("GET", path, null, null); try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { capabilities.set(Arrays.stream((new Gson()).fromJson(in, Capability[].class)) - .collect(Collectors.toMap(Capability::code, Function.identity(), (existingCapability, newCapability) -> existingCapability))); + .collect(Collectors.toMap(c -> c.code().toLowerCase(), Function.identity(), (existingCapability, newCapability) -> existingCapability))); } catch(IOException e) { throw e; } @@ -416,6 +420,7 @@ public String loadFrom(String themeLocation, int timeoutInMilliseconds) { return in.lines().collect(Collectors.joining()); } } catch (IOException e) { + logger.debug(e.getMessage()); return ""; } } @@ -465,7 +470,7 @@ public List technologicalCapabilitiesCompletion() { CompletionItemLabelDetails details = new CompletionItemLabelDetails(); details.setDetail(" " + e.getValue().name()); item.setKind(CompletionItemKind.Property); - item.setLabel(e.getKey()); + item.setLabel(e.getValue().code()); item.setLabelDetails(details); return item; }).toList(); @@ -477,7 +482,7 @@ public List businessCapabilitiesCompletion() { CompletionItemLabelDetails details = new CompletionItemLabelDetails(); details.setDetail(" " + e.getValue().name()); item.setKind(CompletionItemKind.Property); - item.setLabel(e.getKey()); + item.setLabel(e.getValue().code()); item.setLabelDetails(details); return item; }).toList(); @@ -562,7 +567,7 @@ public List cloudFlavorsCompletion(String vegaProject) { } public Hover businessCapabilitiesHover(String code) { - Capability capability = capabilities.get().get(code); + Capability capability = capabilities.get().get(code.toLowerCase()); if (capability != null) { String name = capability.name(); if (name != null) { @@ -579,7 +584,7 @@ public Hover businessCapabilitiesHover(String code) { } public Hover technologicalCapabilitiesHover(String code) { - TechCapability capability = techCapabilities.get().get(code); + TechCapability capability = techCapabilities.get().get(code.toLowerCase()); if (capability != null) { String name = capability.name(); if (name != null) { @@ -659,7 +664,7 @@ public List dynamicViewCompletion(String destination, C4Document container.getComponents().stream() .filter(c -> c.getProperties().getOrDefault("type", "").equalsIgnoreCase("capability")) .map(c -> c.getProperties().get("code")).filter(Objects::nonNull).forEach(c -> { - TechCapability capability = techCapabilities.get().get(c); + TechCapability capability = techCapabilities.get().get(c.toLowerCase()); CompletionItem item = new CompletionItem(); item.setLabel(c); @@ -732,6 +737,27 @@ public Hover getPropertiesHover(List tokens, CursorLocation cursorAt, return null; } + public Stream getDecorations(C4DocumentModel docModel) { + return isApiConfigured() ? docModel.getProperties().stream().mapMulti( (p, consumer) -> { + List tokens = LineTokenizer.tokenize(p.line()); + int character = tokens.get(1).end(); + if(p.name().equalsIgnoreCase("tc")) { + TechCapability tc = techCapabilities.get().get(p.value().toLowerCase()); + if(tc == null && cmdb != null) { + tc = techCapabilities.get().get(cmdb.toLowerCase() + "." + p.value().toLowerCase()); + } + if(tc!= null) { + consumer.accept(new DecoratorRange("# " + tc.name(),new Range(new Position(p.lineNumber() - 1, character), new Position(p.lineNumber() - 1, character)))); + } + } else if(p.name().equalsIgnoreCase("parents")) { + Capability c = capabilities.get().get(p.value().toLowerCase()); + if(c!= null) { + consumer.accept(new DecoratorRange("# " + c.name(),new Range(new Position(p.lineNumber() - 1, character), new Position(p.lineNumber() - 1, character)))); + } + } + }) : Stream.empty(); + } + public List comleteProperties(List tokens, CursorLocation cursor, C4DocumentModel docModel, Position position) { logger.info("completeProperties"); int lineNumberBackward = position.getLine(); @@ -1039,6 +1065,7 @@ private boolean sendTelemetry(String message) { outStream.close(); return true; } catch (Exception e) { + logger.debug(e.getMessage()); return false; } } diff --git a/c4-server/src/main/java/ru/beeatlas/c4/model/C4DocumentManager.java b/c4-server/src/main/java/ru/beeatlas/c4/model/C4DocumentManager.java index 6b30eb4..81d736c 100644 --- a/c4-server/src/main/java/ru/beeatlas/c4/model/C4DocumentManager.java +++ b/c4-server/src/main/java/ru/beeatlas/c4/model/C4DocumentManager.java @@ -230,7 +230,7 @@ public void onException(StructurizrDslParserException e) throws StructurizrDslPa @Override public void onParsedProperty(String name, String value) { if(context != null) { - context.model.addProperty(new C4Property(context.line.number(), name, value)); + context.model.addProperty(new C4Property(context.line.number(), context.line.source(), name, value)); } else { logger.error("onParsedProperty() - Context is null"); } diff --git a/c4-server/src/main/java/ru/beeatlas/c4/model/C4DocumentModel.java b/c4-server/src/main/java/ru/beeatlas/c4/model/C4DocumentModel.java index 42eee0f..4f5e7d4 100644 --- a/c4-server/src/main/java/ru/beeatlas/c4/model/C4DocumentModel.java +++ b/c4-server/src/main/java/ru/beeatlas/c4/model/C4DocumentModel.java @@ -199,7 +199,7 @@ public void setValid(boolean valid) { } public List calculateDecorations() { - return decorations; + return Stream.concat(decorations.stream(), Custom.getInstance().getDecorations(this)).toList(); } public View findViewByKey(String viewKey) throws Exception { @@ -251,6 +251,10 @@ public int getRelationshipsCount() { return relationShipsToLineNumber.size(); } + public List getProperties() { + return properties; + } + public List calculateTokens() { List sorted = tokens.stream().sorted(Comparator.comparing(C4SemanticToken::lineNumber)).toList(); diff --git a/c4-server/src/main/java/ru/beeatlas/c4/model/C4Property.java b/c4-server/src/main/java/ru/beeatlas/c4/model/C4Property.java index 0a6b055..b92eff3 100644 --- a/c4-server/src/main/java/ru/beeatlas/c4/model/C4Property.java +++ b/c4-server/src/main/java/ru/beeatlas/c4/model/C4Property.java @@ -16,5 +16,5 @@ package ru.beeatlas.c4.model; -public record C4Property(int lineNumber, String name, String value) { +public record C4Property(int lineNumber, String line, String name, String value) { } \ No newline at end of file diff --git a/c4-server/src/main/java/ru/beeatlas/c4/utils/MxExporter.java b/c4-server/src/main/java/ru/beeatlas/c4/utils/MxExporter.java index 4144c1d..6d53b83 100644 --- a/c4-server/src/main/java/ru/beeatlas/c4/utils/MxExporter.java +++ b/c4-server/src/main/java/ru/beeatlas/c4/utils/MxExporter.java @@ -26,7 +26,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.Stack; import java.util.UUID; import java.util.stream.Collectors; diff --git a/extension/CHANGELOG.md b/extension/CHANGELOG.md index ae909c7..1a903ac 100644 --- a/extension/CHANGELOG.md +++ b/extension/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## 1.0.16 + +- Using stdin/stdout instead of sockets (during iteraction with lang server) +- Using maven shade (with minimizeJar) instead of maven assembly +- Removing picocli dependency +- Up lsp4j to 1.0.0 +- Minor TypeScript code cleanup +- Some custom code decoration + ## 1.0.15 - Updating JS dependencies diff --git a/extension/esbuild.js b/extension/esbuild.js index aa1e5ec..ccc0f36 100644 --- a/extension/esbuild.js +++ b/extension/esbuild.js @@ -15,6 +15,7 @@ async function main() { outfile: 'dist/extension.js', external: ['vscode'], logLevel: 'warning', + treeShaking: true, plugins: [ /* add to the end of plugins array */ esbuildProblemMatcherPlugin diff --git a/extension/package.json b/extension/package.json index ee9c9a8..5b94eb9 100644 --- a/extension/package.json +++ b/extension/package.json @@ -9,7 +9,7 @@ }, "license": "Apache-2.0", "icon": "images/logo.png", - "version": "1.0.15", + "version": "1.0.16", "engines": { "vscode": "^1.73.0" }, @@ -284,6 +284,7 @@ "activationEvents": [ "onLanguage:c4" ], + "browser": "./dist/extension", "main": "./dist/extension", "devDependencies": { "@hpcc-js/wasm-graphviz": "^1.10.0", @@ -303,7 +304,7 @@ "scripts": { "check-types": "tsc --noEmit", "prepare": "yarn run clean && yarn run build", - "clean": "rimraf lib pack", + "clean": "rimraf --glob \"*.jar\"", "build": "npm run check-types && node esbuild.js --production", "build-server": "cd ../c4-server && mvnw package -f ./pom.xml -Djps.track.ap.dependencies=false -DassembleDirectory=../extension/server -DoutputDirectory=../extension/", "clean-server": "cd ../c4-server && mvnw clean -f ./pom.xml", diff --git a/extension/src/custom/ArchitectureAsACodeView.ts b/extension/src/custom/ArchitectureAsACodeView.ts index 2969f1a..d696cf5 100644 --- a/extension/src/custom/ArchitectureAsACodeView.ts +++ b/extension/src/custom/ArchitectureAsACodeView.ts @@ -1,68 +1,67 @@ /* - Copyright 2025 VimpelCom PJSC + Copyright 2025 VimpelCom PJSC - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ -import * as vscode from 'vscode'; -import { Uri } from 'vscode'; -import * as fs from 'fs'; +import { readFileSync } from 'node:fs'; +import { ExtensionContext, MarkdownString, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, window } from 'vscode'; -class Pattern extends vscode.TreeItem { - label: string; +class Pattern extends TreeItem { + label: string; langId: string; snippetName: string; hover: string[]; - childrens: Pattern[]; + childrens: Pattern[]; } -export class PatternProvider implements vscode.TreeDataProvider { - private uri : Uri; - constructor(context: vscode.ExtensionContext) { - const view = vscode.window.createTreeView('architectureAsACodeView', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true }); +export class PatternProvider implements TreeDataProvider { + private readonly uri: Uri; + constructor(context: ExtensionContext) { + const view = window.createTreeView('architectureAsACodeView', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true }); context.subscriptions.push(view); this.uri = Uri.file(context.asAbsolutePath('snippets.json')); } - getTreeItem(element: Pattern): vscode.TreeItem { + getTreeItem(element: Pattern): TreeItem { return element; - } + } - initChapter(patterns: Pattern[]) : Pattern[] { + initChapter(patterns: Pattern[]): Pattern[] { patterns.forEach(pattern => { if (pattern.childrens.length === 0) { pattern.command = { command: "c4.insert.snippet", title: "Insert Snippet", - arguments:[{ langId: pattern.langId, name: pattern.snippetName }] + arguments: [{ langId: pattern.langId, name: pattern.snippetName }] }; - let tooltip = new vscode.MarkdownString("", true); + let tooltip = new MarkdownString("", true); pattern.hover.forEach(h => tooltip.appendMarkdown(h)); pattern.tooltip = tooltip; } else { - pattern.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; + pattern.collapsibleState = TreeItemCollapsibleState.Collapsed; } this.initChapter(pattern.childrens); - }); - return patterns; - } + }); + return patterns; + } - async getChildren(element?: Pattern): Promise { - if(element) { - return Promise.resolve(element.childrens); + async getChildren(element?: Pattern): Promise { + if (element) { + return element.childrens; } - let data = fs.readFileSync(this.uri.fsPath); + let data = readFileSync(this.uri.fsPath); let pattern = JSON.parse(data.toString()) as Pattern return this.initChapter(pattern.childrens); - } + } } \ No newline at end of file diff --git a/extension/src/custom/ArchitectureCatalogueView.ts b/extension/src/custom/ArchitectureCatalogueView.ts index 6e0253e..66bef2a 100644 --- a/extension/src/custom/ArchitectureCatalogueView.ts +++ b/extension/src/custom/ArchitectureCatalogueView.ts @@ -14,44 +14,43 @@ limitations under the License. */ -import * as vscode from 'vscode'; -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import * as httpm from 'typed-rest-client/HttpClient'; import * as config from '../config'; -import { workspace } from 'vscode'; +import { ExtensionContext, TreeDataProvider, TreeItem, WebviewPanel, workspace, window, TreeItemCollapsibleState, commands, EventEmitter, Event, ViewColumn } from 'vscode'; import { generateHmac } from './hmac'; import { IRequestOptions } from 'typed-rest-client/Interfaces'; import { C4Utils } from '../utils/c4-utils'; +import { HttpClient } from 'typed-rest-client/HttpClient'; +import { basename, join } from 'node:path'; +import { writeFile } from 'node:fs'; -class Item extends vscode.TreeItem { +class Item extends TreeItem { title: string; docs: string; dsl: string; childrens: Item[]; } -export class ArchitectureCatalogueProvider implements vscode.TreeDataProvider { +export class ArchitectureCatalogueProvider implements TreeDataProvider { private readonly INDEX_ID: string = '/index'; private readonly CONTENT_ID: string = '/content/'; private readonly PATH = '/architecture-center'; - private currentPanel: vscode.WebviewPanel | undefined = undefined; + private currentPanel: WebviewPanel | undefined = undefined; private lastDocs: string | undefined = undefined; private readonly initItem: (items: Item[]) => Item[]; private readonly initRoot: () => Promise; - constructor(context: vscode.ExtensionContext) { + constructor(context: ExtensionContext) { - const view = vscode.window.createTreeView('architectureCatalogueView', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true }); + const view = window.createTreeView('architectureCatalogueView', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true }); context.subscriptions.push(view); const options: IRequestOptions = {}; options.ignoreSslError = !(workspace.getConfiguration().get(config.BEELINE_CERT_VERIFICATION) as boolean); const archopsApiUrl = C4Utils.removeTrailingSlash(workspace.getConfiguration().get(config.BEELINE_API_URL) as string); - const httpc = new httpm.HttpClient('vscode-c4-dsl-plugin', [], options); + const httpc = new HttpClient('vscode-c4-dsl-plugin', [], options); this.initItem = (items: Item[]) : Item[] => { items.forEach(item => { @@ -67,7 +66,7 @@ export class ArchitectureCatalogueProvider implements vscode.TreeDataProvider 0) { - item.collapsibleState = vscode.TreeItemCollapsibleState.None; + item.collapsibleState = TreeItemCollapsibleState.None; item.command = { command: "c4.architectureCatalogue.showDescription", title: "Show pattern description", @@ -82,10 +81,10 @@ export class ArchitectureCatalogueProvider implements vscode.TreeDataProvider 0) ? 'leaf' : 'chapter'; + const basenamePath = basename(item.dsl); + item.contextValue = (basenamePath.length > 0) ? 'leaf' : 'chapter'; } else { - item.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; + item.collapsibleState = TreeItemCollapsibleState.Collapsed; item.contextValue = 'chapter'; } @@ -111,24 +110,24 @@ export class ArchitectureCatalogueProvider implements vscode.TreeDataProvider { + commands.registerCommand('c4.architectureCatalogue.refresh', async (...args: string[]) => { this.refresh(); }); - vscode.commands.registerCommand('c4.architectureCatalogue.add', async (element: Item) => { + commands.registerCommand('c4.architectureCatalogue.add', async (element: Item) => { const createFile = (id: string, body: string) => { - const basename = path.basename(id); - if (basename.length > 0) { - const paths = vscode.workspace.workspaceFolders; + const basenamePath = basename(id); + if (basenamePath.length > 0) { + const paths = workspace.workspaceFolders; if (paths !== undefined && paths.length > 0) { - const filepath = path.join(paths[0].uri.fsPath, basename); - fs.writeFile(filepath, body, (error) => { + const filepath = join(paths[0].uri.fsPath, basenamePath); + writeFile(filepath, body, (error) => { if (error) { - vscode.window.showErrorMessage(error.message); + window.showErrorMessage(error.message); } else { - vscode.workspace.openTextDocument(filepath).then((doc) => { vscode.window.showTextDocument(doc); }); - vscode.commands.executeCommand('c4-server.send-pattern-telemetry', { patternId: id, action: 'pattern' }); + workspace.openTextDocument(filepath).then((doc) => { window.showTextDocument(doc); }); + commands.executeCommand('c4-server.send-pattern-telemetry', { patternId: id, action: 'pattern' }); } }); } @@ -150,15 +149,15 @@ export class ArchitectureCatalogueProvider implements vscode.TreeDataProvider { + commands.registerCommand('c4.architectureCatalogue.showDescription', async (...args: string[]) => { - const columnToShowIn = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; + const columnToShowIn = window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined; if (this.currentPanel === undefined) { - this.currentPanel = vscode.window.createWebviewPanel( + this.currentPanel = window.createWebviewPanel( 'architectureCatalogueDescription', args[0], - columnToShowIn || vscode.ViewColumn.One, + columnToShowIn || ViewColumn.One, {} ); } else { @@ -181,7 +180,7 @@ export class ArchitectureCatalogueProvider implements vscode.TreeDataProvider { @@ -192,14 +191,14 @@ export class ArchitectureCatalogueProvider implements vscode.TreeDataProvider = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + private readonly _onDidChangeTreeData: EventEmitter = new EventEmitter(); + readonly onDidChangeTreeData: Event = this._onDidChangeTreeData.event; refresh(): void { this._onDidChangeTreeData.fire(); } - getTreeItem(element: Item): vscode.TreeItem { + getTreeItem(element: Item): TreeItem { return element; } diff --git a/extension/src/custom/BusinessCapabilitiesView.ts b/extension/src/custom/BusinessCapabilitiesView.ts index 876d265..ed4a4ac 100644 --- a/extension/src/custom/BusinessCapabilitiesView.ts +++ b/extension/src/custom/BusinessCapabilitiesView.ts @@ -14,14 +14,14 @@ limitations under the License. */ -import * as vscode from 'vscode'; -import * as httpm from 'typed-rest-client/HttpClient'; import * as config from '../config'; import { IRequestOptions } from 'typed-rest-client/Interfaces'; import { C4Utils } from '../utils'; import { generateHmac } from './hmac'; +import { EventEmitter, ExtensionContext, MarkdownString, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, window, workspace, Event, commands, env } from 'vscode'; +import { HttpClient } from 'typed-rest-client/HttpClient'; -class Item extends vscode.TreeItem { +class Item extends TreeItem { name: string; bcid: string | undefined; hasChildren: boolean | undefined; @@ -35,7 +35,7 @@ class Child { businessCapabilities: Item[]; } -export class BusinessCapabilityProvider implements vscode.TreeDataProvider { +export class BusinessCapabilityProvider implements TreeDataProvider { private readonly PARAMS = '?findBy=CORE'; private readonly ROOT_ID: string = '/business-capability'; private readonly PATH = '/capability/api/v1'; @@ -44,15 +44,15 @@ export class BusinessCapabilityProvider implements vscode.TreeDataProvider private readonly initChild: (items: Item) => Promise; private readonly initRoot: () => Promise; - constructor(context: vscode.ExtensionContext) { + constructor(context: ExtensionContext) { - const view = vscode.window.createTreeView('bcCatalogueView', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true }); + const view = window.createTreeView('bcCatalogueView', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true }); context.subscriptions.push(view); const options: IRequestOptions = {}; - options.ignoreSslError = !(vscode.workspace.getConfiguration().get(config.BEELINE_CERT_VERIFICATION) as boolean); - const archopsApiUrl = C4Utils.removeTrailingSlash(vscode.workspace.getConfiguration().get(config.BEELINE_API_URL) as string); - const httpc = new httpm.HttpClient('vscode-c4-dsl-plugin', [], options); + options.ignoreSslError = !(workspace.getConfiguration().get(config.BEELINE_CERT_VERIFICATION) as boolean); + const archopsApiUrl = C4Utils.removeTrailingSlash(workspace.getConfiguration().get(config.BEELINE_API_URL) as string); + const httpc = new HttpClient('vscode-c4-dsl-plugin', [], options); this.initChild = async (chapter: Item): Promise => { const children: string = `/${chapter.bcid}/children`; @@ -70,12 +70,12 @@ export class BusinessCapabilityProvider implements vscode.TreeDataProvider this.initItem = (items: Item[], istc: boolean): Item[] => { items.forEach(item => { if (item.hasChildren) { - item.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; + item.collapsibleState = TreeItemCollapsibleState.Collapsed; } if (typeof item.description === 'string') { - item.tooltip = new vscode.MarkdownString(item.description); + item.tooltip = new MarkdownString(item.description); } - item.iconPath = (istc) ? vscode.ThemeIcon.File : vscode.ThemeIcon.Folder; + item.iconPath = (istc) ? ThemeIcon.File : ThemeIcon.Folder; item.label = item.name; item.description = item.code; item.bcid = item.id; @@ -96,24 +96,24 @@ export class BusinessCapabilityProvider implements vscode.TreeDataProvider catch((error) => []); }; - vscode.commands.registerCommand('c4.capabilitiesCatalogue.copy', async (element: Item) => { - vscode.env.clipboard.writeText(element.code).then(() => { - vscode.window.showInformationMessage(`Capability code ${element.code} copied to clipboard!`); + commands.registerCommand('c4.capabilitiesCatalogue.copy', async (element: Item) => { + env.clipboard.writeText(element.code).then(() => { + window.showInformationMessage(`Capability code ${element.code} copied to clipboard!`); }, (error) => { - vscode.window.showErrorMessage(`Failed to copy capability code: ${error.message}`); + window.showErrorMessage(`Failed to copy capability code: ${error.message}`); }); }); } - private readonly _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + private readonly _onDidChangeTreeData: EventEmitter = new EventEmitter(); + readonly onDidChangeTreeData: Event = this._onDidChangeTreeData.event; refresh(): void { this._onDidChangeTreeData.fire(); } - getTreeItem(element: Item): vscode.TreeItem { + getTreeItem(element: Item): TreeItem { return element; } diff --git a/extension/src/custom/C4InsertSla.ts b/extension/src/custom/C4InsertSla.ts index 6f5af76..9400319 100644 --- a/extension/src/custom/C4InsertSla.ts +++ b/extension/src/custom/C4InsertSla.ts @@ -22,20 +22,21 @@ import { workspace } from "vscode"; -import * as fs from 'node:fs'; -import * as httpm from 'typed-rest-client/HttpClient'; import { IRequestOptions } from "typed-rest-client/Interfaces"; import { BEELINE_API_URL, BEELINE_CERT_VERIFICATION } from "../config"; import { generateHmac } from "./hmac"; import { CodeLensCommandArgs } from "../types/CodeLensCommandArgs"; import { dirname, join } from 'node:path'; import { C4Utils } from "../utils/c4-utils"; +import { HttpClient } from "typed-rest-client/HttpClient"; +import { existsSync, readFile } from "node:fs"; +import { EOL } from "node:os"; export function c4InsertSla() { commands.registerCommand("c4.insert.sla", async (args : CodeLensCommandArgs) => { const options: IRequestOptions = {}; options.ignoreSslError = !(workspace.getConfiguration().get(BEELINE_CERT_VERIFICATION) as boolean); - const httpc = new httpm.HttpClient('vscode-c4-dsl-plugin', [], options); + const httpc = new HttpClient('vscode-c4-dsl-plugin', [], options); window.withProgress({ location: ProgressLocation.Notification, @@ -54,7 +55,7 @@ export function c4InsertSla() { progress.report({ message: "Формирование SLA..." }); httpc.post(beelineApiUrl + path, content, headers) .then((result) => { return result.readBody() }).then((body) => { - var lines = body.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => " ".repeat(args.padding) + line); + let lines = body.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => " ".repeat(args.padding) + line); const editor = window.activeTextEditor; if (editor) { editor.edit(editBuilder => { @@ -72,8 +73,7 @@ export function c4InsertSla() { return; } } - var os = require('os'); - editBuilder.insert(new Position(args.lastLine, 0), lines.join(os.EOL) + os.EOL); + editBuilder.insert(new Position(args.lastLine, 0), lines.join(EOL) + EOL); }); } }) @@ -98,8 +98,8 @@ export function c4InsertSla() { if(fileName !== undefined) { const directoryPath = dirname(fileName); const fullPath = join(directoryPath, args.apiUrl); - if(fs.existsSync(fullPath)) { - fs.readFile(fullPath, 'utf8', (error, data) => { + if(existsSync(fullPath)) { + readFile(fullPath, 'utf8', (error, data) => { if (error) { window.showErrorMessage(error.message); } else { diff --git a/extension/src/custom/CjCatalogueView.ts b/extension/src/custom/CjCatalogueView.ts index e41efc3..f01d36f 100644 --- a/extension/src/custom/CjCatalogueView.ts +++ b/extension/src/custom/CjCatalogueView.ts @@ -14,30 +14,30 @@ limitations under the License. */ -import * as vscode from 'vscode'; -import * as httpm from 'typed-rest-client/HttpClient'; import * as config from '../config'; import { IRequestOptions } from 'typed-rest-client/Interfaces'; import { C4Utils } from '../utils'; import { generateHmac } from './hmac'; +import { HttpClient } from 'typed-rest-client/HttpClient'; +import { ExtensionContext, TreeDataProvider, TreeItem, TreeItemCollapsibleState, window, workspace } from 'vscode'; -class Cj extends vscode.TreeItem { +class Cj extends TreeItem { name: string; bpmn: boolean; childrens: Cj[]; } -export class CjProvider implements vscode.TreeDataProvider { +export class CjProvider implements TreeDataProvider { private readonly PARAMS = '?sample=ALL'; private readonly CJ_ID = '/cj'; private readonly PATH = '/cx/api/cx/v1/product'; - constructor(context: vscode.ExtensionContext) { - const view = vscode.window.createTreeView('cjCatalogueView', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true }); + constructor(context: ExtensionContext) { + const view = window.createTreeView('cjCatalogueView', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true }); context.subscriptions.push(view); } - getTreeItem(element: Cj): vscode.TreeItem { + getTreeItem(element: Cj): TreeItem { return element; } @@ -45,7 +45,7 @@ export class CjProvider implements vscode.TreeDataProvider { cjs.forEach(cj => { cj.label = cj.name; if (cj.childrens.length === 0) { /* empty */ } else { - cj.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; + cj.collapsibleState = TreeItemCollapsibleState.Collapsed; } this.initChapter(cj.childrens); }); @@ -59,9 +59,9 @@ export class CjProvider implements vscode.TreeDataProvider { const headers = generateHmac('GET', this.PATH + this.CJ_ID); const options: IRequestOptions = {}; - options.ignoreSslError = !(vscode.workspace.getConfiguration().get(config.BEELINE_CERT_VERIFICATION) as boolean); - const beelineApiUrl = C4Utils.removeTrailingSlash(vscode.workspace.getConfiguration().get(config.BEELINE_API_URL) as string); - const httpc = new httpm.HttpClient('vscode-c4-dsl-plugin', [], options); + options.ignoreSslError = !(workspace.getConfiguration().get(config.BEELINE_CERT_VERIFICATION) as boolean); + const beelineApiUrl = C4Utils.removeTrailingSlash(workspace.getConfiguration().get(config.BEELINE_API_URL) as string); + const httpc = new HttpClient('vscode-c4-dsl-plugin', [], options); let path = beelineApiUrl + this.PATH + this.CJ_ID + this.PARAMS; return httpc.get(path, headers). diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 413abc7..1c3eccf 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -26,16 +26,14 @@ import { TextEditor } from "vscode"; -import * as path from "node:path"; -import * as cp from "node:child_process"; -import * as readline from "node:readline"; +import { exec } from "node:child_process"; import { LanguageClientOptions, StateChangeEvent, State } from "vscode-languageclient"; -import { LanguageClient } from "vscode-languageclient/node"; +import { LanguageClient, ServerOptions } from "vscode-languageclient/node"; import { CommandResultCode, ConfigurationOptions, @@ -43,8 +41,6 @@ import { TextDocumentChangeConfig, } from "./types"; import { C4Utils } from "./utils"; -import { PatternProvider } from "./custom/ArchitectureAsACodeView"; -import { ArchitectureCatalogueProvider } from "./custom/ArchitectureCatalogueView"; import { c4InsertSnippet } from "./custom/C4InsertSnippet"; import { c4InsertSla } from "./custom/C4InsertSla"; import { c4ExportDeployment } from "./custom/C4ExportDeployment"; @@ -53,34 +49,34 @@ import { StructurizrPreviewService } from "./services/StructurizrPreviewService" import { CodeLensCommandArgs } from "./types/CodeLensCommandArgs"; import { PreviewService } from "./services/PreviewService"; import { DecorationService } from "./services/DecorationService"; -import { basename, dirname, join } from "node:path"; +import { basename, dirname, join, delimiter } from "node:path"; import { writeFile } from "node:fs"; import { BEELINE_CERT_VERIFICATION } from "./config"; +import { ArchitectureCatalogueProvider } from "./custom/ArchitectureCatalogueView"; +import { PatternProvider } from "./custom/ArchitectureAsACodeView"; import { BusinessCapabilityProvider } from "./custom/BusinessCapabilitiesView"; -let proc: cp.ChildProcess; - export function activate(context: ExtensionContext) { let envJava = process.env; const javaPath = workspace.getConfiguration().get(config.JAVA_PATH) as string; if(javaPath !== undefined && javaPath.length > 0) { - const javaBinPath = path.join(javaPath, "bin"); + const javaBinPath = join(javaPath, "bin"); let envPath = process.env.PATH; if(envPath !== undefined) { - const pathParts = envPath.split(path.delimiter); + const pathParts = envPath.split(delimiter); const filteredPathParts = pathParts.filter(part => !part.toLowerCase().includes('jdk-') && !part.toLowerCase().includes('jre-')); - envPath = filteredPathParts.join(path.delimiter); + envPath = filteredPathParts.join(delimiter); } envJava = { ...process.env, - PATH: `${javaBinPath}${path.delimiter}${envPath}` + PATH: `${javaBinPath}${delimiter}${envPath}` }; } - cp.exec("java -version", { env: envJava }, (err, stdOut, stdErr) => { + exec("java -version", { env: envJava }, (err, stdOut, stdErr) => { if ( err?.message.includes("'java' is not recognized") || err?.message.includes("'java' not found") || @@ -113,10 +109,28 @@ function initExtension(context: ExtensionContext, env: NodeJS.ProcessEnv) { }, }; + const jarPath = context.asAbsolutePath('server.jar'); + + const args = ["-XX:+UseParallelGC", "-XX:TieredStopAtLevel=1", "-Dfile.encoding=UTF8", "-jar", jarPath]; + const opts = (workspace.workspaceFolders) ? { cwd: workspace.workspaceFolders[0].uri.fsPath, shell: true, env : env } : { shell: true, env : env }; + + const serverOptions: ServerOptions = { + run: { + command: 'java', + args: args, + options : opts + }, + debug: { + command: 'java', + args: args, + options : opts + } + }; + const languageClient = new LanguageClient( "c4LanguageClient", "C4 Language Server", - C4Utils.getServerOptions(), + serverOptions, clientOptions ); @@ -143,175 +157,153 @@ function initExtension(context: ExtensionContext, env: NodeJS.ProcessEnv) { } }); - const READY_ECHO = "READY_TO_CONNECT"; const STRUCTURIZ_COM = "https://structurizr.com/json"; statusBarItem.text = "C4 DSL Socket Server is starting up..."; statusBarItem.color = "white"; - const jar = path.join('server','c4-server.jar'); - const jarPath = context.asAbsolutePath(jar); - - const args = ["-Dfile.encoding=UTF8", "-jar", jarPath, "-e=" + READY_ECHO]; - const opts = (workspace.workspaceFolders) ? { cwd: workspace.workspaceFolders[0].uri.fsPath, shell: true, env : env } : { shell: true, env : env }; - proc = cp.spawn("java", args, opts); - - if (proc.stdout) { - const reader = readline.createInterface({ input: proc.stdout, terminal: false, }); - - const startListener = (line: string) => { - if (line.endsWith(READY_ECHO)) { - reader.removeListener("line", startListener); - languageClient.start().then(() => { - - updateServerConfigurationIndent(); - updateServerConfiguration(context); - - commands.registerCommand("c4.workspace.save.json", async () => { - const doc = window.activeTextEditor?.document as TextDocument; - const refreshOptions: RefreshOptions = { - viewKey: undefined, - document: doc.uri.fsPath, - svg: undefined, - mx: undefined - }; - commands.executeCommand("c4-server.get-json", refreshOptions).then(async (callback) => { - const result = callback as CommandResultCode; - if (result.message !== undefined) { - const directoryPath = dirname(doc.uri.fsPath); - const bname = basename('workspace.json'); - const filepath = join(directoryPath, bname); - writeFile(filepath, result.message, function (error) { - if (error) { - window.showErrorMessage(error.message); - } - }); - } - }); - }); - - const statusBarJsonItem = window.createStatusBarItem(StatusBarAlignment.Left, 100); - statusBarJsonItem.command = "c4.workspace.save.json"; - context.subscriptions.push(statusBarJsonItem); - statusBarJsonItem.text = "Write workspace.json"; - - const decType = window.createTextEditorDecorationType({}); - const textDecorations = workspace.getConfiguration().get(config.TEXT_DECORATIONS) as TextDocumentChangeConfig; - - const switchJson = (editor: TextEditor | undefined) => { - if (editor && editor?.document && editor?.document.languageId === "c4" && editor?.document.fileName && editor?.document.fileName.toLowerCase().endsWith('workspace.dsl')) { - statusBarJsonItem.show(); - } else { - statusBarJsonItem.hide(); - } - }; - - if (textDecorations !== "off") { - const decorationService = new DecorationService(decType); - - if (textDecorations === "onSave") { - workspace.onDidSaveTextDocument((savedDocument) => { - decorationService.triggerDecorations(undefined, savedDocument); - }); - } else if (textDecorations === "onChange") { - workspace.onDidChangeTextDocument((changed) => { - decorationService.triggerDecorations(undefined, changed.document); - }); + languageClient.start().then(() => { + + updateServerConfigurationIndent(); + updateServerConfiguration(context); + + commands.registerCommand("c4.workspace.save.json", async () => { + const doc = window.activeTextEditor?.document as TextDocument; + const refreshOptions: RefreshOptions = { + viewKey: undefined, + document: doc.uri.fsPath, + svg: undefined, + mx: undefined + }; + commands.executeCommand("c4-server.get-json", refreshOptions).then(async (callback) => { + const result = callback as CommandResultCode; + if (result.message !== undefined) { + const directoryPath = dirname(doc.uri.fsPath); + const bname = basename('workspace.json'); + const filepath = join(directoryPath, bname); + writeFile(filepath, result.message, function (error) { + if (error) { + window.showErrorMessage(error.message); } + }); + } + }); + }); + + const statusBarJsonItem = window.createStatusBarItem(StatusBarAlignment.Left, 100); + statusBarJsonItem.command = "c4.workspace.save.json"; + context.subscriptions.push(statusBarJsonItem); + statusBarJsonItem.text = "Write workspace.json"; + + const decType = window.createTextEditorDecorationType({}); + const textDecorations = workspace.getConfiguration().get(config.TEXT_DECORATIONS) as TextDocumentChangeConfig; + + const switchJson = (editor: TextEditor | undefined) => { + if (editor?.document && editor?.document.languageId === "c4" && editor?.document.fileName?.toLowerCase().endsWith('workspace.dsl')) { + statusBarJsonItem.show(); + } else { + statusBarJsonItem.hide(); + } + }; - window.onDidChangeActiveTextEditor((editor) => { - editor ??= window.activeTextEditor; - switchJson(editor); - decorationService.triggerDecorations(editor, undefined); - }); - decorationService.triggerDecorations(window.activeTextEditor, undefined); - } else { - window.onDidChangeActiveTextEditor((editor) => { - editor ??= window.activeTextEditor; - switchJson(editor); - }); - } - - commands.executeCommand('setContext', 'extension:c4', true); + if (textDecorations === "off") { + window.onDidChangeActiveTextEditor((editor) => { + editor ??= window.activeTextEditor; + switchJson(editor); + }); + } else { + const decorationService = new DecorationService(decType); - const embeddedPreviewService = new PreviewService(context); - const structurizrPreviewService = new StructurizrPreviewService(STRUCTURIZ_COM); - c4InsertSnippet(); - c4InsertSla(); - c4ExportDeployment(); + if (textDecorations === "onSave") { + workspace.onDidSaveTextDocument((savedDocument) => { + decorationService.triggerDecorations(undefined, savedDocument); + }); + } else if (textDecorations === "onChange") { + workspace.onDidChangeTextDocument((changed) => { + decorationService.triggerDecorations(undefined, changed.document); + }); + } - commands.registerCommand("c4.diagram.export.svg", async () => { - embeddedPreviewService.getSvg(context); - }); + window.onDidChangeActiveTextEditor((editor) => { + editor ??= window.activeTextEditor; + switchJson(editor); + decorationService.triggerDecorations(editor, undefined); + }); + decorationService.triggerDecorations(window.activeTextEditor, undefined); + } - commands.registerCommand("c4.diagram.export.mx", async () => { - embeddedPreviewService.getMx(context); - }); + commands.executeCommand('setContext', 'extension:c4', true); - commands.registerCommand("c4.diagram.import.layout.mx", async () => { - embeddedPreviewService.importLayoutMax(context); - }); + const embeddedPreviewService = new PreviewService(context); + const structurizrPreviewService = new StructurizrPreviewService(STRUCTURIZ_COM); + c4InsertSnippet(); + c4InsertSla(); + c4ExportDeployment(); - commands.registerCommand("c4.show.diagram", async (args : CodeLensCommandArgs) => { + commands.registerCommand("c4.diagram.export.svg", async () => { + embeddedPreviewService.getSvg(context); + }); - const render = workspace.getConfiguration().get(config.DIAGRAM_RENDER) as string; + commands.registerCommand("c4.diagram.export.mx", async () => { + embeddedPreviewService.getMx(context); + }); - if(render === 'https://structurizr.com') { - structurizrPreviewService.currentDiagram = args.diagramKey; - structurizrPreviewService.currentDocument = window.activeTextEditor?.document as TextDocument; - await structurizrPreviewService.updateWebView(args.encodedWorkspace); - } else { - embeddedPreviewService.currentDiagramAsDot = args.diagramAsDot; - embeddedPreviewService.currentDiagram = args.diagramKey; - embeddedPreviewService.currentDocument = window.activeTextEditor?.document as TextDocument; - await embeddedPreviewService.updateWebView(); - } - }); + commands.registerCommand("c4.diagram.import.layout.mx", async () => { + embeddedPreviewService.importLayoutMax(context); + }); - workspace.onDidSaveTextDocument((document: TextDocument) => { - const autolayoutUrl = workspace.getConfiguration().get(config.DIAGRAM_RENDER) as string; - if (autolayoutUrl === 'https://structurizr.com') { - structurizrPreviewService.triggerRefresh(document); - } else { - embeddedPreviewService.triggerRefresh(document); - } - }); + commands.registerCommand("c4.show.diagram", async (args : CodeLensCommandArgs) => { - workspace.onDidChangeConfiguration(event => { - if (event.affectsConfiguration(config.AUTO_FORMAT_INDENT)) { - updateServerConfigurationIndent() - } - updateServerConfiguration(context); - }); + const render = workspace.getConfiguration().get(config.DIAGRAM_RENDER) as string; - const beelineApiUrl = C4Utils.removeTrailingSlash(workspace.getConfiguration().get(config.BEELINE_API_URL) as string); - const beelineApiSecret = workspace.getConfiguration().get(config.BEELINE_API_SECRET) as string; - const beelineApiKey = workspace.getConfiguration().get(config.BEELINE_API_KEY) as string; - if(beelineApiUrl.length === 0 || beelineApiSecret.length === 0 || beelineApiKey.length === 0) { - window.showInformationMessage( - 'You haven\'t filled out all the extension settings yet. Complete the configuration to use all the features.', - 'Open settings' - ).then(selection => { - if (selection === 'Open settings') { - commands.executeCommand( 'workbench.action.openSettings', '@ext:vimpelcom.c4-varp'); - } - }); - } - }); + if(render === 'https://structurizr.com') { + structurizrPreviewService.currentDiagram = args.diagramKey; + structurizrPreviewService.currentDocument = window.activeTextEditor?.document as TextDocument; + await structurizrPreviewService.updateWebView(args.encodedWorkspace); + } else { + embeddedPreviewService.currentDiagramAsDot = args.diagramAsDot; + embeddedPreviewService.currentDiagram = args.diagramKey; + embeddedPreviewService.currentDocument = window.activeTextEditor?.document as TextDocument; + await embeddedPreviewService.updateWebView(); } - }; + }); + + workspace.onDidSaveTextDocument((document: TextDocument) => { + const autolayoutUrl = workspace.getConfiguration().get(config.DIAGRAM_RENDER) as string; + if (autolayoutUrl === 'https://structurizr.com') { + structurizrPreviewService.triggerRefresh(document); + } else { + embeddedPreviewService.triggerRefresh(document); + } + }); - reader.on('line', startListener); + workspace.onDidChangeConfiguration(event => { + if (event.affectsConfiguration(config.AUTO_FORMAT_INDENT)) { + updateServerConfigurationIndent() + } + updateServerConfiguration(context); + }); + + const beelineApiUrl = C4Utils.removeTrailingSlash(workspace.getConfiguration().get(config.BEELINE_API_URL) as string); + const beelineApiSecret = workspace.getConfiguration().get(config.BEELINE_API_SECRET) as string; + const beelineApiKey = workspace.getConfiguration().get(config.BEELINE_API_KEY) as string; + if(beelineApiUrl.length === 0 || beelineApiSecret.length === 0 || beelineApiKey.length === 0) { + window.showInformationMessage( + 'You haven\'t filled out all the extension settings yet. Complete the configuration to use all the features.', + 'Open settings' + ).then(selection => { + if (selection === 'Open settings') { + commands.executeCommand( 'workbench.action.openSettings', '@ext:vimpelcom.c4-varp'); + } + }); + } new PatternProvider(context); new BusinessCapabilityProvider(context); const architectureCatalogueProvider : ArchitectureCatalogueProvider = new ArchitectureCatalogueProvider(context); architectureCatalogueProvider.refresh(); - } else { - statusBarItem.text = "Connection to C4 DSL Socket Server could not be established"; - statusBarItem.color = "red"; - } + }); } function updateServerConfigurationIndent() { @@ -327,10 +319,4 @@ export function updateServerConfiguration(context: ExtensionContext) { process.env.NODE_TLS_REJECT_UNAUTHORIZED = (workspace.getConfiguration().get(BEELINE_CERT_VERIFICATION) as boolean) ? "1" : "0"; commands.executeCommand("c4-server.configuration", configOptions); -} - -export function deactivate() { - if (proc) { - proc.kill("SIGINT"); - } } \ No newline at end of file diff --git a/extension/src/services/DecorationService.ts b/extension/src/services/DecorationService.ts index 69a9ac1..bb0458f 100644 --- a/extension/src/services/DecorationService.ts +++ b/extension/src/services/DecorationService.ts @@ -25,7 +25,7 @@ import { import { CommandResultTextDecorations, DecoratedRange } from "../types"; class DecorationService { - private decorationType: TextEditorDecorationType; + private readonly decorationType: TextEditorDecorationType; constructor(decorationType: TextEditorDecorationType) { this.decorationType = decorationType; @@ -35,13 +35,9 @@ class DecorationService { editor: TextEditor | undefined, document: TextDocument | undefined ) { - if (!editor) { - editor = window.activeTextEditor; - } - if (!document) { - document = editor?.document; - } - if (editor && document && document.languageId === "c4") { + editor ??= window.activeTextEditor; + document ??= editor?.document; + if (editor && document?.languageId === "c4") { commands.executeCommand("c4-server.text-decorations", { uri: document.uri.path, }).then((callback) => { editor?.setDecorations( this.decorationType, diff --git a/extension/src/services/StructurizrPreviewService.ts b/extension/src/services/StructurizrPreviewService.ts index d54ccf7..cd1b8d1 100644 --- a/extension/src/services/StructurizrPreviewService.ts +++ b/extension/src/services/StructurizrPreviewService.ts @@ -25,12 +25,12 @@ import { RefreshOptions } from "../types/RefreshOptions"; import { CommandResultCode } from "../types/CommandResultCode"; class StructurizrPreviewService { - private renderService: string; + private readonly renderService: string; private panel: WebviewPanel | undefined; private _currentDiagram: string; private _currentDocument: TextDocument; - private VIEW_TYPE: string = 'Structurizr Preview'; + private readonly VIEW_TYPE: string = 'Structurizr Preview'; constructor(renderService: string) { this.renderService = renderService; diff --git a/extension/src/utils/c4-utils.ts b/extension/src/utils/c4-utils.ts index b19224f..65e5066 100644 --- a/extension/src/utils/c4-utils.ts +++ b/extension/src/utils/c4-utils.ts @@ -14,31 +14,12 @@ limitations under the License. */ -import * as net from "net"; -import { ServerOptions, StreamInfo } from "vscode-languageclient/node"; - class C4Utils { static getJavaVersion(versionDetails: string): number { - const matchedRegex = versionDetails.match(/version\s"\d\d/i)?.at(0); - if (matchedRegex) { - const version = parseInt( + const matchedRegex = new RegExp(/version\s"\d\d/i).exec(versionDetails)?.at(0); + return (matchedRegex) ? Number.parseInt( matchedRegex.slice('version "'.length, matchedRegex.length) - ); - return version; - } - return 0; - } - - static getServerOptions(): ServerOptions { - const serverDebugOptions = () => { - let socket = net.connect({ port: 5008 }); - let result: StreamInfo = { - writer: socket, - reader: socket, - }; - return Promise.resolve(result); - }; - return serverDebugOptions; + ) : 0; } static removeTrailingSlash(str: string): string {