Skip to content

Commit 1a289b5

Browse files
committed
vscode: General cleanup in the extension code
1 parent 3f01cb3 commit 1a289b5

File tree

5 files changed

+164
-152
lines changed

5 files changed

+164
-152
lines changed

src/control-flow/generic-cfg-builder.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,18 @@ export class GenericCFGBuilder {
4141
if (entry) this.builder.addEdge(startNode, entry);
4242
if (exit) this.builder.addEdge(exit, endNode);
4343

44-
4544
// Make sure the end of the function is linked to the last piece of code, not to the top of the function.
46-
const lastStatement = bodySyntax.namedChildren[bodySyntax.namedChildren.length - 1];
45+
const lastStatement =
46+
bodySyntax.namedChildren[bodySyntax.namedChildren.length - 1];
4747
if (lastStatement) {
48-
this.nodeMapper.linkGap(lastStatement, functionNode, { includeTo: true, reverse: true });
49-
console.log(lastStatement.text)
48+
this.nodeMapper.linkGap(lastStatement, functionNode, {
49+
includeTo: true,
50+
reverse: true,
51+
});
52+
console.log(lastStatement.text);
5053
}
5154
}
5255

53-
5456
return {
5557
graph: this.builder.getGraph(),
5658
entry: startNode,

src/vscode/extension.ts

Lines changed: 56 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { graphToDot } from "../control-flow/render";
77
import { simplifyCFG, trimFor } from "../control-flow/graph-ops";
88
import { newCFGBuilder, type Language } from "../control-flow/cfg";
99
import { mergeNodeAttrs } from "../control-flow/cfg-defs";
10+
import { OverviewViewProvider } from "./overview-view";
1011

1112
let graphviz: Graphviz;
1213
interface SupportedLanguage {
@@ -110,12 +111,43 @@ function getCurrentCode(): {
110111
return { code, languageId, language };
111112
}
112113

114+
type Settings = { flatSwitch?: boolean; simplify?: boolean };
115+
function loadSettings(): Settings {
116+
const config = vscode.workspace.getConfiguration("functionGraphOverview");
117+
return {
118+
flatSwitch: config.get("flatSwitch"),
119+
simplify: config.get("simplify"),
120+
};
121+
}
122+
123+
function getFunctionAtPosition(
124+
tree: Parser.Tree,
125+
position: vscode.Position,
126+
language: Language,
127+
): Parser.SyntaxNode | null {
128+
let syntax: SyntaxNode | null = tree.rootNode.descendantForPosition({
129+
row: position.line,
130+
column: position.character,
131+
});
132+
133+
while (syntax) {
134+
if (functionNodeTypes[language].includes(syntax.type)) {
135+
break;
136+
}
137+
syntax = syntax.parent;
138+
}
139+
return syntax;
140+
}
113141
// This method is called when your extension is activated
114142
// Your extension is activated the very first time the command is executed
115143
export async function activate(context: vscode.ExtensionContext) {
116144
graphviz = await Graphviz.load();
145+
const helloWorldSvg = graphviz.dot("digraph G { Hello -> World }");
117146

118-
const provider = new OverviewViewProvider(context.extensionUri);
147+
const provider = new OverviewViewProvider(
148+
context.extensionUri,
149+
helloWorldSvg,
150+
);
119151

120152
context.subscriptions.push(
121153
vscode.window.registerWebviewViewProvider(
@@ -136,6 +168,11 @@ export async function activate(context: vscode.ExtensionContext) {
136168
(event: vscode.TextEditorSelectionChangeEvent): void => {
137169
const editor = event.textEditor;
138170
const position = editor.selection.active;
171+
// const offset = editor.document.offsetAt(position);
172+
173+
console.log(
174+
`Cursor position changed: Line ${position.line + 1}, Column ${position.character + 1}`,
175+
);
139176

140177
const { code, languageId, language } = getCurrentCode() ?? {};
141178
if (!code || !languageId || !language) {
@@ -144,62 +181,28 @@ export async function activate(context: vscode.ExtensionContext) {
144181

145182
const tree = parsers[language].parse(code);
146183

147-
console.log(
148-
`Cursor position changed: Line ${position.line + 1}, Column ${position.character + 1}`,
149-
);
150-
let node: SyntaxNode | null = tree.rootNode.descendantForPosition({
151-
row: position.line,
152-
column: position.character,
153-
});
184+
const functionSyntax = getFunctionAtPosition(tree, position, language);
185+
186+
if (!functionSyntax) return;
154187

155-
while (node) {
156-
if (functionNodeTypes[language].includes(node.type)) {
157-
break;
158-
}
159-
node = node.parent;
188+
console.log(functionSyntax);
189+
const nameSyntax = functionSyntax.childForFieldName("name");
190+
if (nameSyntax) {
191+
console.log("Currently in", nameSyntax.text);
160192
}
161193

162-
if (node) {
163-
console.log(node);
164-
const nameNode = node.childForFieldName("name");
165-
if (nameNode) {
166-
const name = editor.document.getText(
167-
new vscode.Range(
168-
new vscode.Position(
169-
nameNode.startPosition.row,
170-
nameNode.startPosition.column,
171-
),
172-
new vscode.Position(
173-
nameNode.endPosition.row,
174-
nameNode.endPosition.column,
175-
),
176-
),
177-
);
178-
console.log("Currently in", name);
179-
}
180-
const flatSwitch = Boolean(
181-
vscode.workspace
182-
.getConfiguration("functionGraphOverview")
183-
.get("flatSwitch"),
184-
);
185-
const language = idToLanguage(languageId);
186-
if (!language) {
187-
return;
188-
}
189-
const builder = newCFGBuilder(language, { flatSwitch });
190-
let cfg = builder.buildCFG(node);
191-
cfg = trimFor(cfg);
192-
if (
193-
vscode.workspace
194-
.getConfiguration("functionGraphOverview")
195-
.get("simplify")
196-
) {
197-
cfg = simplifyCFG(cfg, mergeNodeAttrs);
198-
}
199-
const dot = graphToDot(cfg);
200-
const svg = graphviz.dot(dot);
201-
provider.setSVG(svg);
194+
const { flatSwitch, simplify } = loadSettings();
195+
const builder = newCFGBuilder(language, { flatSwitch });
196+
let cfg = builder.buildCFG(functionSyntax);
197+
cfg = trimFor(cfg);
198+
if (simplify) {
199+
cfg = simplifyCFG(cfg, mergeNodeAttrs);
202200
}
201+
202+
const dot = graphToDot(cfg);
203+
const svg = graphviz.dot(dot);
204+
205+
provider.setSVG(svg);
203206
},
204207
);
205208

@@ -210,96 +213,3 @@ export async function activate(context: vscode.ExtensionContext) {
210213
export function deactivate() {}
211214

212215
//------------------------------------------------
213-
214-
class OverviewViewProvider implements vscode.WebviewViewProvider {
215-
public static readonly viewType = "functionGraphOverview.overview";
216-
217-
private _view?: vscode.WebviewView;
218-
219-
constructor(private readonly _extensionUri: vscode.Uri) {}
220-
221-
public setSVG(svg: string) {
222-
if (this._view) {
223-
this._view.webview.postMessage({ type: "svgImage", svg });
224-
}
225-
}
226-
227-
resolveWebviewView(
228-
webviewView: vscode.WebviewView,
229-
_context: vscode.WebviewViewResolveContext,
230-
_token: vscode.CancellationToken,
231-
): Thenable<void> | void {
232-
this._view = webviewView;
233-
234-
webviewView.webview.options = {
235-
// Allow scripts in the webview
236-
enableScripts: true,
237-
238-
localResourceRoots: [this._extensionUri],
239-
};
240-
241-
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
242-
}
243-
private _getHtmlForWebview(webview: vscode.Webview): string {
244-
// Get the local path to main script run in the webview, then convert it to a uri we can use in the webview.
245-
const scriptUri = webview.asWebviewUri(
246-
vscode.Uri.joinPath(this._extensionUri, "webview-content", "main.js"),
247-
);
248-
249-
// Do the same for the stylesheet.
250-
const styleResetUri = webview.asWebviewUri(
251-
vscode.Uri.joinPath(this._extensionUri, "webview-content", "reset.css"),
252-
);
253-
const styleVSCodeUri = webview.asWebviewUri(
254-
vscode.Uri.joinPath(this._extensionUri, "webview-content", "vscode.css"),
255-
);
256-
const styleMainUri = webview.asWebviewUri(
257-
vscode.Uri.joinPath(this._extensionUri, "webview-content", "main.css"),
258-
);
259-
260-
// Use a nonce to only allow a specific script to be run.
261-
const nonce = getNonce();
262-
263-
const svg = graphviz.dot("digraph G { Hello -> World }");
264-
return `<!DOCTYPE html>
265-
<html lang="en">
266-
<head>
267-
<meta charset="UTF-8">
268-
269-
<!--
270-
Use a content security policy to only allow loading styles from our extension directory,
271-
and only allow scripts that have a specific nonce.
272-
(See the 'webview-sample' extension sample for img-src content security policy examples)
273-
-->
274-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
275-
276-
<link href="${styleResetUri}" rel="stylesheet">
277-
<link href="${styleVSCodeUri}" rel="stylesheet">
278-
<link href="${styleMainUri}" rel="stylesheet">
279-
280-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
281-
<title>Overview</title>
282-
<style>
283-
284-
</style>
285-
</head>
286-
<body>
287-
<div id="overview">
288-
${svg}
289-
</div>
290-
291-
<script nonce="${nonce}" src="${scriptUri}"></script>
292-
</body>
293-
</html>`;
294-
}
295-
}
296-
297-
function getNonce() {
298-
let text = "";
299-
const possible =
300-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
301-
for (let i = 0; i < 32; i++) {
302-
text += possible.charAt(Math.floor(Math.random() * possible.length));
303-
}
304-
return text;
305-
}

src/vscode/overview-view.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as vscode from "vscode";
2+
3+
export class OverviewViewProvider implements vscode.WebviewViewProvider {
4+
public static readonly viewType = "functionGraphOverview.overview";
5+
private readonly helloWorldSvg: string;
6+
private _view?: vscode.WebviewView;
7+
8+
constructor(
9+
private readonly _extensionUri: vscode.Uri,
10+
helloWorldSvg: string,
11+
) {
12+
this.helloWorldSvg = helloWorldSvg;
13+
}
14+
15+
public setSVG(svg: string) {
16+
if (this._view) {
17+
this._view.webview.postMessage({ type: "svgImage", svg });
18+
}
19+
}
20+
21+
resolveWebviewView(
22+
webviewView: vscode.WebviewView,
23+
_context: vscode.WebviewViewResolveContext,
24+
_token: vscode.CancellationToken,
25+
): Thenable<void> | void {
26+
this._view = webviewView;
27+
28+
webviewView.webview.options = {
29+
// Allow scripts in the webview
30+
enableScripts: true,
31+
32+
localResourceRoots: [this._extensionUri],
33+
};
34+
35+
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
36+
}
37+
private _getHtmlForWebview(webview: vscode.Webview): string {
38+
// Get the local path to main script run in the webview, then convert it to a uri we can use in the webview.
39+
const scriptUri = webview.asWebviewUri(
40+
vscode.Uri.joinPath(this._extensionUri, "webview-content", "main.js"),
41+
);
42+
43+
// Do the same for the stylesheet.
44+
const styleResetUri = webview.asWebviewUri(
45+
vscode.Uri.joinPath(this._extensionUri, "webview-content", "reset.css"),
46+
);
47+
const styleVSCodeUri = webview.asWebviewUri(
48+
vscode.Uri.joinPath(this._extensionUri, "webview-content", "vscode.css"),
49+
);
50+
const styleMainUri = webview.asWebviewUri(
51+
vscode.Uri.joinPath(this._extensionUri, "webview-content", "main.css"),
52+
);
53+
54+
// Use a nonce to only allow a specific script to be run.
55+
const nonce = getNonce();
56+
57+
return `<!DOCTYPE html>
58+
<html lang="en">
59+
<head>
60+
<meta charset="UTF-8">
61+
62+
<!--
63+
Use a content security policy to only allow loading styles from our extension directory,
64+
and only allow scripts that have a specific nonce.
65+
(See the 'webview-sample' extension sample for img-src content security policy examples)
66+
-->
67+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
68+
69+
<link href="${styleResetUri}" rel="stylesheet">
70+
<link href="${styleVSCodeUri}" rel="stylesheet">
71+
<link href="${styleMainUri}" rel="stylesheet">
72+
73+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
74+
<title>Overview</title>
75+
<style>
76+
77+
</style>
78+
</head>
79+
<body>
80+
<div id="overview">${this.helloWorldSvg}</div>
81+
82+
<script nonce="${nonce}" src="${scriptUri}"></script>
83+
</body>
84+
</html>`;
85+
}
86+
}
87+
88+
function getNonce() {
89+
let text = "";
90+
const possible =
91+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
92+
for (let i = 0; i < 32; i++) {
93+
text += possible.charAt(Math.floor(Math.random() * possible.length));
94+
}
95+
return text;
96+
}

webview-content/main.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ svg {
1919
width: 100%;
2020
height: 100%;
2121
}
22+
23+
.highlight {
24+
filter: brightness(5%);
25+
}

webview-content/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@
2020
*/
2121
function displaySVG(svgMarkup) {
2222
const div = document.querySelector("#overview");
23-
div.innerHTML = svgMarkup;
23+
if (div) div.innerHTML = svgMarkup;
2424
}
2525
})();

0 commit comments

Comments
 (0)