Skip to content

Commit 9e72714

Browse files
committed
Basic saveAsDot functionality now working for module hierarchy.
1 parent e35bc36 commit 9e72714

File tree

9 files changed

+96
-3
lines changed

9 files changed

+96
-3
lines changed

src/commands/showHierarchyBase.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CommandBase } from '@commands';
2-
import { Config, DgmlManager, FileSystemUtils } from '@src';
2+
import { Config, DgmlManager, FileSystemUtils, GraphVizManager } from '@src';
33
import { Edge, Node } from '@model';
44
import { Base64 } from 'js-base64';
55
import * as fs from 'fs';
@@ -111,6 +111,17 @@ export class ShowHierarchyBase extends CommandBase {
111111
return fixedDirection;
112112
}
113113

114+
protected saveAsGraphViz(graphVizFilename: string, messageText: string, graphType: string, popMessageText: string) {
115+
const graphVizManager = new GraphVizManager();
116+
const fileContent = graphVizManager.createGraphVizDiagram(graphType, this.nodes, this.edges);
117+
118+
// Write the prettified xml string to the ReadMe-ProjectStructure.dgml file.
119+
var directoryPath: string = this.fsUtils.getWorkspaceFolder();
120+
this.fsUtils.writeFile(path.join(directoryPath, graphVizFilename), fileContent, () => {
121+
vscode.window.setStatusBarMessage(popMessageText, 10000);
122+
});
123+
}
124+
114125
protected generateHtmlContent(webview: vscode.Webview, outputJsFilename: string): string {
115126
let htmlContent = fs.readFileSync(this.extensionContext?.asAbsolutePath(path.join('templates', this.templateHtmlFilename)), 'utf8');
116127

src/commands/showModuleHierarchy.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export class ShowModuleHierarchy extends ShowHierarchyBase {
1818
case 'saveAsDgml':
1919
this.saveAsDgml(this.config.moduleHierarchyDgmlGraphFilename, message.text, `'The modules hierarchy has been analyzed and a Directed Graph Markup Language (dgml) file '${this.config.moduleHierarchyDgmlGraphFilename}' has been created'`);
2020
return;
21+
case 'saveAsDot':
22+
this.saveAsGraphViz('ModuleHierarchy.dot', message.text, 'moduleHierarchy', `'The modules hierarchy has been analyzed and a Directed Graph Markup Language (dgml) file 'ModuleHierarchy.dot' has been created'`);
23+
return;
2124
}
2225
},
2326
undefined,

src/dgmlManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ArrowType, BoundingBox, Category, Edge, NetworkNode, Node, NodeType, Po
22

33
export class DgmlManager {
44

5-
public createNewDirectedGraph(domImpl: DOMImplementation, direction: string, layout: string, zoomLevel: string) {
5+
public createNewDirectedGraph(domImpl: DOMImplementation, direction: string, layout: string, zoomLevel: string): Document {
66
let xmlDoc: Document = domImpl.createDocument('', null, null);
77
const root = xmlDoc.createElement("DirectedGraph");
88
if (direction.length > 0) {

src/graphvizManager.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Edge, Node } from "@model";
2+
3+
export class GraphVizManager {
4+
5+
public createGraphVizDiagram(graphType: string, nodes: Node[], edges: Edge[]): string {
6+
const digraphNodes = this.generateDigraphNodes(nodes);
7+
const digraphEdges = this.generateDigraphEdges(edges);
8+
const graphVizDigraph = this.addRootNode(graphType, digraphNodes, digraphEdges);
9+
10+
return graphVizDigraph;
11+
}
12+
private addRootNode(graphType: string, digraphNodes: string, digraphEdges: string): string {
13+
return `digraph ${graphType} {\n ${digraphNodes}\n ${digraphEdges}\n}`;
14+
}
15+
private generateDigraphNodes(nodes: Node[]): string {
16+
return nodes.map(node => node.toGraphViz()).join('\n ');
17+
}
18+
private generateDigraphEdges(edges: Edge[]): string {
19+
return edges.map(edge => edge.toGraphViz()).join('\n ');
20+
}
21+
}
22+

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export * from './componentManager';
33
export * from './config';
44
export * from './dgmlManager';
55
export * from './filesystemUtils';
6+
export * from './graphvizManager';
67
export * from './moduleManager';

src/model/Edge.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,26 @@ export class Edge {
1515
public arrowType: ArrowType;
1616

1717
public toJsonString(): string {
18-
let arrowColorAttr = `, color: "${this.getEdgeTypeColor(this.arrowType)}"`;;
18+
let arrowColorAttr = `, color: "${this.getEdgeTypeColor(this.arrowType)}"`;
1919
return `{from: "${this.source}", to: "${this.target}", arrows: arrowAttr${arrowColorAttr} }`;
2020
}
2121

22+
public toGraphViz(): string {
23+
const regex = /\W/g;
24+
const source = this.source.replace(regex, '_');
25+
const target = this.target.replace(regex, '_');
26+
const attributes: string[] = [];
27+
const color = this.getEdgeTypeColor(this.arrowType);
28+
if (color) {
29+
attributes.push(`color="${color}"`);
30+
}
31+
let attributesStr: string = '';
32+
if (attributes.length > 0) {
33+
attributesStr = ` [${attributes.join(', ')}]`;
34+
}
35+
return `${source} -> ${target}${attributesStr};`;
36+
}
37+
2238
public getEdgeTypeColor(arrowType: ArrowType): string {
2339
let edgeTypeColor = '';
2440
switch (arrowType) {

src/model/Node.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@ export class Node {
5252
return `{id: "${this.id}", label: "${label}"${nodeColorAttr}${nodeShapeAttr}}`;
5353
}
5454

55+
public toGraphViz(): string {
56+
const regex = /\W/g;
57+
const id = this.id.replace(regex, '_');
58+
const attributes: string[] = [];
59+
attributes.push(`label="${this.name}"`);
60+
const color = this.getNodeTypeColor(this.nodeType);
61+
if (color) {
62+
attributes.push(`color="${color}"`);
63+
attributes.push(`style="filled"`);
64+
}
65+
let attributesStr: string = '';
66+
if (attributes.length > 0) {
67+
attributesStr = ` [${attributes.join(', ')}]`;
68+
}
69+
return `${id}${attributesStr};`;
70+
}
71+
5572
public getNodeTypeColor(nodeType: NodeType): string {
5673
let nodeTypeColor = '';
5774
switch (nodeType) {

templates/showHierarchy_Template.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<div class="dropdown-content">
1919
<a href="#" id="saveAsPngButton">Save as Png</a>
2020
<a href="#" Id="saveAsDgmlButton">Save as Dgml</a>
21+
<a href="#" Id="saveAsDotButton">Save as Dot</a>
2122
</div>
2223
</div>
2324
<input type="button" id="saveSelectionAsPngButton" value="Save selection as png">

templates/showHierarchy_Template.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
saveAsPngButton.addEventListener('click', saveAsPng);
5858
const saveAsDgmlButton = document.getElementById('saveAsDgmlButton');
5959
saveAsDgmlButton.addEventListener('click', saveAsDgml);
60+
const saveAsDotButton = document.getElementById('saveAsDotButton');
61+
saveAsDotButton.addEventListener('click', saveAsDot);
6062
const saveSelectionAsPngButton = document.getElementById('saveSelectionAsPngButton');
6163
saveSelectionAsPngButton.addEventListener('click', saveSelectionAsPng);
6264
const copyToClipboardButton = document.getElementById('copyToClipboardButton');
@@ -266,6 +268,26 @@
266268
});
267269
}
268270

271+
function saveAsDot() {
272+
const nodeExport = {};
273+
nodes.forEach(node => {
274+
nodeExport[node.id] = {
275+
id: node.id,
276+
label: cleanLabel(node.label),
277+
position: network.getPosition(node.id),
278+
boundingBox: network.getBoundingBox(node.id)
279+
};
280+
});
281+
const direction = hierarchicalOptionsDirectionSelect.value ? hierarchicalOptionsDirectionSelect.value : 'UD';
282+
vscode.postMessage({
283+
command: 'saveAsDot',
284+
text: JSON.stringify({
285+
nodes: nodeExport,
286+
direction: direction
287+
})
288+
});
289+
}
290+
269291
function cleanLabel(label) {
270292
let regex = /(<([^>]+)>)/ig;
271293
let cleanedLabel = label.replace(regex, '');

0 commit comments

Comments
 (0)