Skip to content

Commit 501c16f

Browse files
committed
Initial work for graphs
Signed-off-by: worksofliam <[email protected]>
1 parent 69d7c08 commit 501c16f

File tree

2 files changed

+171
-1
lines changed

2 files changed

+171
-1
lines changed

src/views/cytoscape/index.ts

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { ViewColumn, window } from "vscode";
2+
3+
type Styles = {[key: string]: string};
4+
5+
export interface Element {
6+
data: {id: string, label: string},
7+
style: Styles
8+
}
9+
10+
export interface Edge {
11+
data: {id: string, source: string, target: string}
12+
}
13+
14+
interface NewNode {
15+
label: string,
16+
styles?: Styles,
17+
parent?: string,
18+
data?: any;
19+
}
20+
21+
const randomId = () => Math.random().toString(36).substring(7);
22+
23+
export class CytoscapeGraph {
24+
private elementData = new Map<string, any>();
25+
private elements: Element[] = [];
26+
private edges: Edge[] = [];
27+
28+
constructor() {}
29+
30+
addNode(node: NewNode): string {
31+
const id = randomId(); // TODO: is this unique enough?
32+
33+
if (node.data) {
34+
this.elementData.set(id, node.data);
35+
}
36+
37+
this.elements.push({
38+
data: {id, label: node.label},
39+
style: node.styles || {}
40+
});
41+
42+
if (node.parent) {
43+
this.edges.push({
44+
data: {id: randomId(), source: node.parent, target: id}
45+
});
46+
}
47+
48+
return id;
49+
}
50+
51+
createView(title: string) {
52+
const webview = window.createWebviewPanel(`c`, title, {viewColumn: ViewColumn.One}, {enableScripts: true, retainContextWhenHidden: true});
53+
webview.webview.html = this.getHtml();
54+
55+
return webview;
56+
}
57+
58+
private getHtml(): string {
59+
return /*html*/`
60+
<!DOCTYPE html>
61+
<html lang="en">
62+
63+
<head>
64+
<meta charset="UTF-8">
65+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66+
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.23.0/cytoscape.min.js"></script>
67+
<style>
68+
/* html,
69+
body {
70+
margin: 0;
71+
padding: 0;
72+
height: 100%;
73+
width: 100%;
74+
overflow: hidden;
75+
} */
76+
77+
.diagram-container {
78+
position: absolute;
79+
top: 0;
80+
left: 0;
81+
width: 100%;
82+
height: 100%;
83+
border: none;
84+
margin: 0;
85+
}
86+
</style>
87+
</head>
88+
89+
<body>
90+
<div class="diagram-container" id="diagramContainer"></div>
91+
92+
<script>
93+
document.addEventListener("DOMContentLoaded", function () {
94+
// Initialize Cytoscape
95+
const cy = cytoscape({
96+
container: document.getElementById('diagramContainer'),
97+
98+
elements: ${JSON.stringify([...this.elements, ...this.edges])},
99+
100+
style: [
101+
{
102+
selector: 'node',
103+
style: {
104+
'width': '120px',
105+
'height': '60px',
106+
'background-color': 'var(--vscode-list-activeSelectionBackground)',
107+
'color': 'var(--vscode-list-activeSelectionForeground)',
108+
'label': 'data(label)',
109+
'text-valign': 'center',
110+
'text-halign': 'center',
111+
'font-size': '14px',
112+
'text-wrap': 'wrap',
113+
'text-max-width': '100px'
114+
}
115+
},
116+
{
117+
selector: 'edge',
118+
style: {
119+
'width': 2,
120+
'line-color': '#5c96bc',
121+
'target-arrow-color': '#5c96bc',
122+
'target-arrow-shape': 'triangle',
123+
'curve-style': 'bezier'
124+
}
125+
}
126+
],
127+
128+
// Layout options
129+
layout: {
130+
name: 'breadthfirst',
131+
directed: true, // Directional tree
132+
padding: 10, // Padding around the graph
133+
spacingFactor: 1.5 // Spacing between nodes
134+
}
135+
});
136+
137+
// Add click event to show alert for nodes
138+
cy.on('tap', 'node', function (evt) {
139+
const node = evt.target;
140+
console.log("You clicked: " + node.data('label'));
141+
});
142+
});
143+
</script>
144+
</body>
145+
146+
</html>
147+
`;
148+
}
149+
}

src/views/results/index.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { JobManager } from "../../config";
88
import Document from "../../language/sql/document";
99
import { ObjectRef, ParsedEmbeddedStatement, StatementGroup, StatementType } from "../../language/sql/types";
1010
import Statement from "../../language/sql/statement";
11-
import { ExplainTree } from "./explain/nodes";
11+
import { ExplainNode, ExplainTree } from "./explain/nodes";
1212
import { DoveResultsView, ExplainTreeItem } from "./explain/doveResultsView";
1313
import { DoveNodeView, PropertyNode } from "./explain/doveNodeView";
1414
import { DoveTreeDecorationProvider } from "./explain/doveTreeDecorationProvider";
@@ -17,6 +17,7 @@ import { generateSqlForAdvisedIndexes } from "./explain/advice";
1717
import { updateStatusBar } from "../jobManager/statusBar";
1818
import { ExplainType } from "@ibm/mapepire-js/dist/src/types";
1919
import { DbCache } from "../../language/providers/logic/cache";
20+
import { CytoscapeGraph } from "../cytoscape";
2021

2122
export type StatementQualifier = "statement" | "update" | "explain" | "onlyexplain" | "json" | "csv" | "cl" | "sql";
2223

@@ -256,6 +257,26 @@ async function runHandler(options?: StatementInfo) {
256257
const rootNode = doveResultsView.setRootNode(topLevel);
257258
doveNodeView.setNode(rootNode.explainNode);
258259
doveTreeDecorationProvider.updateTreeItems(rootNode);
260+
261+
const graph = new CytoscapeGraph();
262+
263+
function addNode(node: ExplainNode, parent?: string) {
264+
const id = graph.addNode({
265+
label: node.title,
266+
parent: parent,
267+
});
268+
269+
if (node.children) {
270+
for (const child of node.children) {
271+
addNode(child, id);
272+
}
273+
}
274+
}
275+
276+
addNode(topLevel);
277+
278+
const webview = graph.createView(`Explain Graph`);
279+
259280
} else {
260281
vscode.window.showInformationMessage(`No job currently selected.`);
261282
}

0 commit comments

Comments
 (0)