Skip to content

Commit a4b26fc

Browse files
committed
render page: added support for direct-from-graph rendering
1 parent be2b381 commit a4b26fc

File tree

2 files changed

+105
-22
lines changed

2 files changed

+105
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
1111
- The JetBrains plugin can now change settings: flat switch, simplification, highlighting, and color scheme
1212
- `/render` page to render code directly from GitHub, given a URL with a line number.
1313
- `render-graph.ts` script to render a graph from a JSON file exported from code
14+
- `/render` page can now render a graph provided to it directly.
1415

1516
### Fixed
1617

src/render/src/App.svelte

Lines changed: 104 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,26 @@
77
import type Parser from "web-tree-sitter";
88
import { type SyntaxNode } from "web-tree-sitter";
99
import { type Language, newCFGBuilder } from "../../control-flow/cfg";
10-
import { type CFG, mergeNodeAttrs } from "../../control-flow/cfg-defs";
10+
import {
11+
type CFG,
12+
type GraphEdge,
13+
type GraphNode,
14+
mergeNodeAttrs,
15+
} from "../../control-flow/cfg-defs";
1116
import { simplifyCFG, trimFor } from "../../control-flow/graph-ops";
1217
import { Graphviz } from "@hpcc-js/wasm-graphviz";
1318
import { graphToDot } from "../../control-flow/render";
1419
import {
20+
type ColorScheme,
1521
getDarkColorList,
1622
getLightColorList,
1723
listToScheme,
1824
} from "../../control-flow/colors";
1925
import Panzoom, { type PanzoomObject } from "@panzoom/panzoom";
2026
import { onMount } from "svelte";
27+
import { MultiDirectedGraph } from "graphology";
28+
29+
let codeUrl: string | undefined;
2130
2231
/**
2332
* A reference to a function on GitHub
@@ -26,7 +35,7 @@
2635
/**
2736
* The URL for the raw file on GitHub
2837
*/
29-
rawURL: string;
38+
rawUrl: string;
3039
/**
3140
* The line-number for the function
3241
*/
@@ -45,12 +54,12 @@
4554
throw new Error("Missing line number.");
4655
}
4756
48-
const rawURL = githubURL.replace(
57+
const rawUrl = githubURL.replace(
4958
/(?<host>https:\/\/github.com\/)(?<project>\w+\/\w+\/)(blob\/)(?<path>.*)(#L\d+)/,
5059
"https://raw.githubusercontent.com/$<project>$<path>",
5160
);
5261
53-
return { line, rawURL };
62+
return { line, rawUrl };
5463
}
5564
5665
/**
@@ -106,30 +115,103 @@
106115
107116
let rawSVG: string | undefined;
108117
109-
async function render() {
110-
const urlSearchParams = new URLSearchParams(window.location.search);
111-
const githubUrl = urlSearchParams.get("github") ?? "";
118+
type GithubParams = {
119+
type: "GitHub";
120+
rawUrl: string;
121+
codeUrl: string;
122+
line: number;
123+
};
124+
type GraphParams = {
125+
type: "Graph";
126+
rawUrl: string;
127+
};
128+
type Params = (GithubParams | GraphParams) & {
129+
colorScheme: ColorScheme;
130+
colors: "light" | "dark";
131+
};
132+
133+
function parseUrlSearchParams(urlSearchParams: URLSearchParams): Params {
134+
const githubUrl = urlSearchParams.get("github");
112135
const colors = urlSearchParams.get("colors") ?? "dark";
113-
if (colors !== "light" && colors !== "dark") {
114-
throw new Error(`Unsupported color scheme ${colors}`);
136+
const graphUrl = urlSearchParams.get("graph");
137+
138+
if (colors !== "dark" && colors !== "light") {
139+
throw new Error("Invalid color scheme");
140+
}
141+
if (!(githubUrl || graphUrl)) {
142+
throw new Error("No URL provided");
115143
}
144+
if (githubUrl && graphUrl) {
145+
throw new Error("Too many URLs provided");
146+
}
147+
116148
const colorScheme = getColorScheme(colors);
117-
setBackgroundColor(colors);
118149
119-
const { line, rawURL } = parseGithubUrl(githubUrl);
120-
const response = await fetch(rawURL);
150+
if (githubUrl) {
151+
const { line, rawUrl } = parseGithubUrl(githubUrl);
152+
return { type: "GitHub", rawUrl, line, colorScheme, colors, codeUrl };
153+
}
154+
return {
155+
type: "Graph",
156+
rawUrl: graphUrl,
157+
colorScheme: colorScheme,
158+
colors,
159+
};
160+
}
161+
162+
async function createGitHubCFG(ghParams: GithubParams): Promise<CFG> {
163+
const { rawUrl, line } = ghParams;
164+
const response = await fetch(rawUrl);
121165
const code = await response.text();
122166
// We assume that the raw URL always ends with the file extension
123-
const language = getLanguage(rawURL);
167+
const language = getLanguage(rawUrl);
124168
125169
const func = await getFunctionByLine(code, language, line);
126170
if (!func) {
127171
throw new Error(`Unable to find function on line ${line}`);
128172
}
129173
130-
const cfg = buildCFG(func, language);
174+
return buildCFG(func, language);
175+
}
176+
177+
async function createGraphCFG(graphParams: GraphParams): Promise<CFG> {
178+
const { rawUrl } = graphParams;
179+
const response = await fetch(rawUrl);
180+
const jsonData = await response.json();
181+
const graph = new MultiDirectedGraph<GraphNode, GraphEdge>();
182+
graph.import(jsonData);
183+
184+
const entry = graph.findNode(
185+
(node, _attributes) => graph.inDegree(node) === 0,
186+
);
187+
if (!entry) {
188+
throw new Error("No entry found");
189+
}
190+
return { graph, entry, offsetToNode: [] };
191+
}
192+
193+
async function createCFG(params: Params): Promise<CFG> {
194+
switch (params.type) {
195+
case "GitHub":
196+
return createGitHubCFG(params);
197+
case "Graph":
198+
return createGraphCFG(params);
199+
}
200+
}
201+
202+
async function render() {
203+
const urlSearchParams = new URLSearchParams(window.location.search);
204+
const params = parseUrlSearchParams(urlSearchParams);
205+
setBackgroundColor(params.colors);
206+
if (params.type === "GitHub") {
207+
codeUrl = params.codeUrl;
208+
}
209+
210+
const cfg = await createCFG(params);
131211
const graphviz = await Graphviz.load();
132-
rawSVG = graphviz.dot(graphToDot(cfg, false, undefined, colorScheme));
212+
rawSVG = graphviz.dot(
213+
graphToDot(cfg, false, undefined, params.colorScheme),
214+
);
133215
return rawSVG;
134216
}
135217
@@ -150,12 +232,7 @@
150232
}
151233
152234
function openCode() {
153-
const urlSearchParams = new URLSearchParams(window.location.search);
154-
const githubUrl = urlSearchParams.get("github") ?? "";
155-
156-
if (!githubUrl) return;
157-
158-
window.open(githubUrl, "_blank").focus();
235+
window.open(codeUrl, "_blank").focus();
159236
}
160237
161238
function saveSVG() {
@@ -190,7 +267,12 @@
190267
<div class="controlsContainer">
191268
<div class="controls">
192269
<button on:click={resetView}>Reset View</button>
193-
<button on:click={openCode}>Open Code</button>
270+
<button
271+
on:click={openCode}
272+
disabled={!Boolean(codeUrl)}
273+
title={Boolean(codeUrl) ? "" : "Only available for GitHub code"}
274+
>Open Code</button
275+
>
194276
<button on:click={saveSVG}>Download SVG</button>
195277
</div>
196278
</div>

0 commit comments

Comments
 (0)