diff --git a/cmakels/CMakeLists.txt b/cmakels/CMakeLists.txt
index ce7003c..7c886ba 100644
--- a/cmakels/CMakeLists.txt
+++ b/cmakels/CMakeLists.txt
@@ -12,6 +12,9 @@ if(CMAKE_BUILD_TYPE STREQUAL Debug)
if((CMAKE_C_COMPILER_ID STREQUAL "GNU") OR (CMAKE_C_COMPILER_ID STREQUAL "Clang"))
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
endif()
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-limit-debug-info")
+ endif()
endif()
include(CTest)
diff --git a/cmakels/clients/vscode/.vscode/launch.json b/cmakels/clients/vscode/.vscode/launch.json
index 6018202..16643a8 100644
--- a/cmakels/clients/vscode/.vscode/launch.json
+++ b/cmakels/clients/vscode/.vscode/launch.json
@@ -4,7 +4,8 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
- "configurations": [{
+ "configurations": [
+ {
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
diff --git a/cmakels/clients/vscode/content/browseTemplate.html b/cmakels/clients/vscode/content/browseTemplate.html
new file mode 100644
index 0000000..9f86a3f
--- /dev/null
+++ b/cmakels/clients/vscode/content/browseTemplate.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ TITLE
+
+
+ PLACEHOLDER
+
+
\ No newline at end of file
diff --git a/cmakels/clients/vscode/content/icon.svg b/cmakels/clients/vscode/content/icon.svg
new file mode 100644
index 0000000..501479e
--- /dev/null
+++ b/cmakels/clients/vscode/content/icon.svg
@@ -0,0 +1,134 @@
+
+
diff --git a/cmakels/clients/vscode/content/previewTemplate.html b/cmakels/clients/vscode/content/previewTemplate.html
new file mode 100644
index 0000000..2dc0c1a
--- /dev/null
+++ b/cmakels/clients/vscode/content/previewTemplate.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ Graph preview
+
+
+
+
+ %
+
+
+
+
+
+
+
+
+
+ PLACEHOLDER
+
+
diff --git a/cmakels/clients/vscode/content/scaling.js b/cmakels/clients/vscode/content/scaling.js
new file mode 100644
index 0000000..c5660f3
--- /dev/null
+++ b/cmakels/clients/vscode/content/scaling.js
@@ -0,0 +1,148 @@
+var originalWidth = 100;
+var originalWidthUnit = "%";
+
+var originalHeight = 100;
+var originalHeightUnit = "%";
+
+var svg = null;
+
+var scale = 1;
+var fitToHeightToggledOn = false;
+var fitToWidthToggledOn = false;
+
+var vscode = null;
+try {
+ vscode = acquireVsCodeApi();
+}catch(error){
+ console.error(error);
+ // swallow, so in the script can be tested in a browser
+}
+
+function initializeScale(initialScale, initialFitToWidthMode, initialFitToHeightMode) {
+ var svgEls = document.getElementsByTagName("svg");
+ if (svgEls.length < 1) {
+ console.error("Cannot find any 'svg' element in the document.");
+ return;
+ }
+
+ svg = svgEls[0];
+ var sizePattern = /^([\d.]+)(em|px|%|cm|mm|in|pt|pc)$/g;
+
+ var match = sizePattern.exec(svg.getAttribute("width"));
+ if (match) {
+ originalWidth = match[1];
+ originalWidthUnit = match[2];
+ }
+
+ sizePattern.lastIndex = -1;
+ var match = sizePattern.exec(svg.getAttribute("height"));
+ if (match) {
+ originalHeight = match[1];
+ originalHeightUnit = match[2];
+ }
+
+ // apply initial values
+ setScale(initialScale);
+ if (initialFitToWidthMode) fitToWidth();
+ setFitToWidthMode(initialFitToWidthMode);
+ if (initialFitToHeightMode) fitToHeight();
+ setFitToHeightMode(initialFitToHeightMode);
+
+ // update the html, but do not send message to extension
+ update(false);
+}
+
+function larger() {
+ untoggleModes();
+ scale*=1.5;
+ update();
+}
+
+function smaller() {
+ untoggleModes();
+ scale/=1.5;
+ update();
+}
+
+function original() {
+ untoggleModes();
+ scale=1;
+ update();
+}
+
+/**
+ * Used when the user sets the scale manually, or the page is re-initialized.
+ */
+function setScale(value) {
+ scale = value;
+ untoggleModes();
+ update();
+}
+
+function fitToWidth() {
+ redefineAsPx();
+ setFitToHeightMode(false);
+ scale=(window.innerWidth-30)/originalWidth;
+ update();
+}
+
+function fitToHeight() {
+ redefineAsPx();
+ setFitToWidthMode(false);
+ scale=(window.innerHeight-80)/originalHeight;
+ update();
+}
+
+const toggledOnStyle = "background-color: black;color: white;";
+
+function untoggleModes() {
+ setFitToWidthMode(false);
+ setFitToHeightMode(false);
+}
+
+function setFitToHeightMode(value) {
+ if (fitToHeightToggledOn == value) return;
+ fitToHeightToggledOn = value;
+ var newStyle = fitToHeightToggledOn ? toggledOnStyle : "";
+ document.getElementById("fitToHeight").setAttribute("style", newStyle);
+
+ // post message to the extension, so the fit-to-height toggle is respected after the webview is updated
+ postMessage({command: 'fitToHeight', value: fitToHeightToggledOn});
+}
+
+function setFitToWidthMode(value) {
+ if (fitToWidthToggledOn == value) return;
+ fitToWidthToggledOn = value;
+ var newStyle = fitToWidthToggledOn ? toggledOnStyle : "";
+ document.getElementById("fitToWidth").setAttribute("style", newStyle);
+
+ // post message to the extension, so the fit-to-width toggle is respected after the webview is updated
+ postMessage({command: 'fitToWidth', value: fitToWidthToggledOn});
+}
+
+function redefineAsPx() {
+ originalWidthUnit = originalHeightUnit = "px";
+}
+
+function update(sendMessage=true) {
+ if (svg) {
+ svg.setAttribute("style", "width: "+(originalWidth*scale)+originalWidthUnit+
+ "; height: "+(originalHeight*scale)+originalHeightUnit);
+ }
+ document.getElementById("scalePercent").setAttribute("value", (scale*100).toFixed(0));
+
+ // post message to the extension, so the scale is respected after the webview is updated
+ if (sendMessage) postMessage({command: 'scale', value: scale});
+}
+
+function exportSvg() {
+ postMessage({command: 'export'})
+}
+
+function openInBrowser() {
+ postMessage({command: 'open'})
+}
+
+function postMessage(message) {
+ if (vscode) vscode.postMessage(message);
+}
\ No newline at end of file
diff --git a/cmakels/clients/vscode/package-lock.json b/cmakels/clients/vscode/package-lock.json
index dc46d85..b1094e0 100644
--- a/cmakels/clients/vscode/package-lock.json
+++ b/cmakels/clients/vscode/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "cmakels-client",
- "version": "0.0.1",
+ "version": "0.0.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -478,6 +478,11 @@
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true
},
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0="
+ },
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -657,6 +662,19 @@
"wrappy": "1"
}
},
+ "opn": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz",
+ "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==",
+ "requires": {
+ "is-wsl": "^1.1.0"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+ },
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -814,6 +832,14 @@
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"dev": true
},
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
@@ -925,6 +951,11 @@
"extsprintf": "^1.2.0"
}
},
+ "viz.js": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/viz.js/-/viz.js-2.1.2.tgz",
+ "integrity": "sha512-UO6CPAuEMJ8oNR0gLLNl+wUiIzQUsyUOp8SyyDKTqVRBtq7kk1VnFmIZW8QufjxGrGEuI+LVR7p/C7uEKy0LQw=="
+ },
"vscode": {
"version": "1.1.33",
"resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.33.tgz",
diff --git a/cmakels/clients/vscode/package.json b/cmakels/clients/vscode/package.json
index 051a46b..6b3daa1 100644
--- a/cmakels/clients/vscode/package.json
+++ b/cmakels/clients/vscode/package.json
@@ -53,6 +53,11 @@
"command": "cmakels.restart",
"title": "cmakels: Restart Language Server",
"description": "Restart the running instance of the language server"
+ },
+ {
+ "command": "cmakels.target_dependencies",
+ "title": "cmakels: Get target dependencies",
+ "description": "Get target dependencies"
}
]
},
@@ -64,7 +69,8 @@
"test": "npm run compile && node ./node_modules/vscode/bin/test"
},
"dependencies": {
- "vscode-languageclient": "^4.1.4"
+ "vscode-languageclient": "^4.1.4",
+ "viz.js": "^2.0.0"
},
"devDependencies": {
"typescript": "^3.3.1",
@@ -72,5 +78,6 @@
"tslint": "^5.12.1",
"@types/node": "^10.12.21",
"@types/mocha": "^2.2.42"
- }
+ },
+ "extensionDependencies": []
}
diff --git a/cmakels/clients/vscode/src/extension.ts b/cmakels/clients/vscode/src/extension.ts
index e15d05f..8dc8cb6 100644
--- a/cmakels/clients/vscode/src/extension.ts
+++ b/cmakels/clients/vscode/src/extension.ts
@@ -1,11 +1,18 @@
-import { workspace, ExtensionContext, commands } from 'vscode';
+import { workspace, ExtensionContext, commands, window, ViewColumn } from 'vscode';
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
+ TextDocumentPositionParams,
+ TextDocumentIdentifier,
+ RequestType,
+ Range,
+ MarkupContent
} from 'vscode-languageclient';
+import { GraphvizPreviewGenerator } from './vscode-graphviz/GraphvizPreviewGenerator';
+
/**
* Method to get workspace configuration option
* @param option name of the option (e.g. for cmakels.path should be path)
@@ -19,6 +26,88 @@ function getConfig(option: string, defaultValue?: any): T {
let client: LanguageClient;
+
+namespace CustomDependenciesRequest {
+ export const type =
+ new RequestType(
+ 'custom/dependencies');
+}
+
+type DependencyNodeId = string | number;
+
+/**
+ * The result of a dependency request.
+ */
+interface Dependencies {
+ /**
+ * The dependency graph as a list of `DependencyNode`s ...
+ */
+ nodes: DependencyNode[];
+
+ /**
+ * ... and a list of edges between nodes.
+ */
+ edges: DependencyEdge[];
+
+ /**
+ * An optional root, identifies the root node, if any.
+ */
+ root: DependencyNodeId;
+
+ /**
+ * An optional range is a range inside a text document
+ * that is used to visualize a dependency request, e.g. by changing the background color.
+ */
+ range?: Range;
+}
+
+/**
+ * A node in the dependency graph
+ */
+interface DependencyNode {
+ /**
+ * Identifier of the node
+ */
+ id: DependencyNodeId;
+
+ /**
+ * The node's content.
+ */
+ content: MarkupContent;
+}
+
+/**
+ * An edge in the dependency graph
+ */
+interface DependencyEdge {
+ /**
+ * Parent
+ */
+ from: DependencyNodeId;
+
+ /**
+ * Child
+ */
+ to: DependencyNodeId;
+
+ /**
+ * Edge kind
+ */
+ kind: number;
+}
+
+/**
+ * Edge kind.
+ * TODO: instead of graphical representation, the kind could be a semantic kind.
+ * But not sure if it could be expressed universal enough.
+ */
+namespace DependencyEdgeKind {
+ export const Solid = 1;
+ export const Dashed = 2;
+ export const Dotted = 3;
+ export const Bold = 4;
+}
+
export function activate(context: ExtensionContext) {
let executablePath = getConfig('path');
@@ -56,6 +145,49 @@ export function activate(context: ExtensionContext) {
client.start();
}));
+ context.subscriptions.push(commands.registerCommand('cmakels.target_dependencies', async () => {
+ if(window.activeTextEditor) {
+ let identifier : TextDocumentIdentifier =
+ { uri: window.activeTextEditor.document.uri.toString()};
+
+ let params :TextDocumentPositionParams = {
+ textDocument: identifier,
+ position: window.activeTextEditor.selection.active};
+
+ client.sendRequest(CustomDependenciesRequest.type, params ).then((result) => {
+ // console.log("yeah: " + result.dependencies[0].id);
+ const graphvizPreviewGenerator = new GraphvizPreviewGenerator(context);
+ let dotgraph:string = "digraph graphname {\n";
+ dotgraph += "node [color=purple]\n";
+ for (let e of result.edges) {
+ // if(d.children) {
+ // for(let child of d.children) {
+ dotgraph += "\"" + e.from + "\"->\"" + e.to + "\" [color=\"blue\" style=\"";
+ switch(e.kind) {
+ case DependencyEdgeKind.Bold:
+ dotgraph += "bold";
+ break;
+ case DependencyEdgeKind.Dashed:
+ dotgraph += "dashed";
+ break;
+ case DependencyEdgeKind.Solid:
+ dotgraph += "solid";
+ break;
+ case DependencyEdgeKind.Dotted:
+ dotgraph += "dotted";
+ break;
+ }
+ dotgraph +="\"];";
+ // }
+ // }
+ }
+ dotgraph += "}";
+ graphvizPreviewGenerator.revealOrCreatePreview("my_target", dotgraph, ViewColumn.Beside);
+
+ });
+ }
+ }));
+
client.start();
}
diff --git a/cmakels/clients/vscode/src/vscode-graphviz/ContentUtils.ts b/cmakels/clients/vscode/src/vscode-graphviz/ContentUtils.ts
new file mode 100644
index 0000000..8283dc3
--- /dev/null
+++ b/cmakels/clients/vscode/src/vscode-graphviz/ContentUtils.ts
@@ -0,0 +1,16 @@
+import { ExtensionContext } from "vscode";
+import * as path from "path";
+var fs = require("fs");
+
+export const CONTENT_FOLDER = "content";
+
+export async function getPreviewTemplate(context: ExtensionContext, templateName: string): Promise {
+ let previewPath = context.asAbsolutePath(path.join(CONTENT_FOLDER, templateName));
+
+ return new Promise((resolve, reject) => {
+ fs.readFile(previewPath, "utf8", function (err:any, data:any) {
+ if (err) reject(err);
+ else resolve(data);
+ });
+ });
+}
diff --git a/cmakels/clients/vscode/src/vscode-graphviz/GraphvizPreviewGenerator.ts b/cmakels/clients/vscode/src/vscode-graphviz/GraphvizPreviewGenerator.ts
new file mode 100644
index 0000000..4cb70da
--- /dev/null
+++ b/cmakels/clients/vscode/src/vscode-graphviz/GraphvizPreviewGenerator.ts
@@ -0,0 +1,192 @@
+import { ExtensionContext, TextDocument, window, ViewColumn, Uri, WebviewPanel, workspace, Disposable } from "vscode";
+const { Module, render } = require('viz.js/full.render.js');
+let Viz = require("viz.js");
+import * as path from "path";
+// import { SvgExporter } from "./SvgExporter";
+// import { OpenInBrowser } from "./OpenInBrowser";
+import { getPreviewTemplate, CONTENT_FOLDER } from "./ContentUtils";
+var fs = require("fs");
+
+export class GraphvizPreviewGenerator extends Disposable {
+
+ webviewPanels = new Map();
+
+ // timeout: NodeJS.Timer;
+
+ constructor(private context: ExtensionContext) {
+ super(() => this.dispose());
+ }
+
+ // setNeedsRebuild(uri: Uri, needsRebuild: boolean): void {
+ // let panel = this.webviewPanels.get(uri);
+
+ // if (panel) {
+ // panel.setNeedsRebuild(needsRebuild);
+
+ // this.resetTimeout();
+ // }
+ // }
+
+ // resetTimeout(): void {
+ // if(this.timeout) {
+ // clearTimeout(this.timeout);
+ // }
+ // this.timeout = setTimeout(() => this.rebuild(), 1000);
+ // }
+
+ // dispose(): void {
+ // clearTimeout(this.timeout);
+ // }
+
+ // rebuild(): void {
+ // this.webviewPanels.forEach(panel => {
+ // if(panel.getNeedsRebuild() && panel.getPanel().visible)
+ // this.updateContent(panel, workspace.textDocuments.find(doc => doc.uri == panel.uri));
+ // });
+ // }
+
+ async revealOrCreatePreview(id: string, doc: string, displayColumn: ViewColumn): Promise {
+ let previewPanel = this.webviewPanels.get(id);
+
+ if (previewPanel) {
+ previewPanel.reveal(displayColumn);
+ }
+ else {
+ previewPanel = this.createPreviewPanel(doc, displayColumn);
+ this.webviewPanels.set(id, previewPanel);
+ // when the user closes the tab, remove the panel
+ previewPanel.getPanel().onDidDispose(() => this.webviewPanels.delete(id), undefined, this.context.subscriptions);
+ // when the pane becomes visible again, refresh it
+ // previewPanel.getPanel().onDidChangeViewState(_ => this.rebuild());
+
+ // previewPanel.getPanel().webview.onDidReceiveMessage(e => this.handleMessage(previewPanel, e), undefined, this.context.subscriptions);
+ }
+
+ this.updateContent(previewPanel, doc);
+ }
+
+ handleMessage(previewPanel: PreviewPanel, message: any): void {
+ console.log(`Message received from the webview: ${message.command}`);
+
+ switch(message.command){
+ case 'scale':
+ previewPanel.setScale(message.value);
+ break;
+ case 'fitToHeight':
+ previewPanel.setFitToHeight(message.value);
+ break;
+ case 'fitToWidth':
+ previewPanel.setFitToWidth(message.value);
+ break;
+ // case 'export':
+ // new SvgExporter().export(previewPanel.uri);
+ // break;
+ // case 'open':
+ // new OpenInBrowser(this.context).open(previewPanel.uri);
+ // break;
+ default:
+ console.warn('Unexpected command: ' + message.command);
+ }
+ }
+
+ createPreviewPanel(doc: string, displayColumn: ViewColumn): PreviewPanel {
+ let previewTitle = 'Preview: '; //${path.basename(window.activeTextEditor.document.fileName)}'`;
+
+ let webViewPanel = window.createWebviewPanel('graphvizPreview', previewTitle, displayColumn, {
+ enableFindWidget: true,
+ enableScripts: true,
+ localResourceRoots: [Uri.file(path.join(this.context.extensionPath, "content"))]
+ });
+
+ webViewPanel.iconPath = Uri.file(this.context.asAbsolutePath("content/icon.svg"));
+
+ return new PreviewPanel(doc, webViewPanel);
+ }
+
+ async updateContent(previewPanel: PreviewPanel, doc: string) {
+ if(!previewPanel.getPanel().webview.html) {
+ previewPanel.getPanel().webview.html = "Please wait...";
+ }
+ // previewPanel.setNeedsRebuild(false);
+ previewPanel.getPanel().webview.html = await this.getPreviewHtml(previewPanel, doc);
+ }
+
+ toSvg(doc: string): Thenable | string {
+ return new Viz({Module, render}).renderString(doc);
+ }
+
+ private async getPreviewHtml(previewPanel: PreviewPanel, doc: string): Promise {
+ let templateHtml = await getPreviewTemplate(this.context, "previewTemplate.html");
+
+ // change resource URLs to vscode-resource:
+ templateHtml = templateHtml.replace(/