Skip to content

Commit 6741588

Browse files
committed
vscode: Learned to highlight the current node
1 parent 1a289b5 commit 6741588

File tree

2 files changed

+65
-16
lines changed

2 files changed

+65
-16
lines changed

src/control-flow/render.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ import { MultiDirectedGraph } from "graphology";
66
class RenderContext {
77
public readonly verbose: boolean;
88
private readonly backlinks: { from: string; to: string }[];
9-
10-
constructor(verbose: boolean, backlinks: { from: string; to: string }[]) {
9+
private readonly highlightedNode: string | undefined;
10+
constructor(
11+
verbose: boolean,
12+
backlinks: { from: string; to: string }[],
13+
highlightedNode?: string,
14+
) {
1115
this.verbose = verbose;
1216
this.backlinks = backlinks;
17+
this.highlightedNode = highlightedNode;
1318
}
1419

1520
public isBacklink(from: string, to: string): boolean {
@@ -19,6 +24,9 @@ class RenderContext {
1924
) != -1
2025
);
2126
}
27+
public isHighlighted(node: string): boolean {
28+
return node == this.highlightedNode;
29+
}
2230
}
2331

2432
/**
@@ -181,10 +189,18 @@ function renderSubgraphs(
181189
return dotContent;
182190
}
183191

184-
export function graphToDot(cfg: CFG, verbose: boolean = false): string {
192+
export function graphToDot(
193+
cfg: CFG,
194+
verbose: boolean = false,
195+
nodeToHighlight?: string,
196+
): string {
185197
const hierarchy = buildHierarchy(cfg);
186198
const backlinks = detectBacklinks(cfg.graph, cfg.entry);
187-
return renderHierarchy(cfg, hierarchy, new RenderContext(verbose, backlinks));
199+
return renderHierarchy(
200+
cfg,
201+
hierarchy,
202+
new RenderContext(verbose, backlinks, nodeToHighlight),
203+
);
188204
}
189205

190206
export function graphToLineNumbers(cfg: CFG): Map<string, number> {
@@ -316,5 +332,8 @@ function renderNode(
316332
graph.getNodeAttribute(node, "lines") * 0.3,
317333
minHeight,
318334
);
335+
if (context.isHighlighted(node)) {
336+
dotAttrs.fillcolor = "black";
337+
}
319338
return ` ${node} [${formatStyle(dotAttrs)}];\n`;
320339
}

src/vscode/extension.ts

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ import { Graphviz } from "@hpcc-js/wasm-graphviz";
66
import { graphToDot } from "../control-flow/render";
77
import { simplifyCFG, trimFor } from "../control-flow/graph-ops";
88
import { newCFGBuilder, type Language } from "../control-flow/cfg";
9-
import { mergeNodeAttrs } from "../control-flow/cfg-defs";
9+
import {
10+
mergeNodeAttrs,
11+
remapNodeTargets,
12+
type CFG,
13+
} from "../control-flow/cfg-defs";
1014
import { OverviewViewProvider } from "./overview-view";
15+
import { getValue } from "../control-flow/ranges";
1116

1217
let graphviz: Graphviz;
1318
interface SupportedLanguage {
@@ -111,14 +116,20 @@ function getCurrentCode(): {
111116
return { code, languageId, language };
112117
}
113118

114-
type Settings = { flatSwitch?: boolean; simplify?: boolean };
119+
type Settings = { flatSwitch: boolean; simplify: boolean };
115120
function loadSettings(): Settings {
116121
const config = vscode.workspace.getConfiguration("functionGraphOverview");
122+
117123
return {
118-
flatSwitch: config.get("flatSwitch"),
119-
simplify: config.get("simplify"),
124+
flatSwitch: config.get("flatSwitch") ?? false,
125+
simplify: config.get("simplify") ?? false,
120126
};
121127
}
128+
type CFGKey = { functionText: string; flatSwitch: boolean; simplify: boolean };
129+
130+
function isSameKey(a: CFGKey, b: CFGKey): boolean {
131+
return JSON.stringify(a) === JSON.stringify(b);
132+
}
122133

123134
function getFunctionAtPosition(
124135
tree: Parser.Tree,
@@ -164,11 +175,14 @@ export async function activate(context: vscode.ExtensionContext) {
164175

165176
const parsers = await initializeParsers(context);
166177

178+
let cfgKey: CFGKey | undefined;
179+
let savedCFG: CFG;
180+
167181
const cursorMove = vscode.window.onDidChangeTextEditorSelection(
168182
(event: vscode.TextEditorSelectionChangeEvent): void => {
169183
const editor = event.textEditor;
170184
const position = editor.selection.active;
171-
// const offset = editor.document.offsetAt(position);
185+
const offset = editor.document.offsetAt(position);
172186

173187
console.log(
174188
`Cursor position changed: Line ${position.line + 1}, Column ${position.character + 1}`,
@@ -182,7 +196,6 @@ export async function activate(context: vscode.ExtensionContext) {
182196
const tree = parsers[language].parse(code);
183197

184198
const functionSyntax = getFunctionAtPosition(tree, position, language);
185-
186199
if (!functionSyntax) return;
187200

188201
console.log(functionSyntax);
@@ -192,14 +205,31 @@ export async function activate(context: vscode.ExtensionContext) {
192205
}
193206

194207
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);
208+
// We'd like to avoid re-running CFG generation for a function if nothing changed.
209+
const newKey: CFGKey = {
210+
flatSwitch,
211+
simplify,
212+
functionText: functionSyntax.text,
213+
};
214+
let cfg: CFG;
215+
if (cfgKey && isSameKey(newKey, cfgKey)) {
216+
cfg = savedCFG;
217+
} else {
218+
cfgKey = newKey;
219+
220+
const builder = newCFGBuilder(language, { flatSwitch });
221+
cfg = builder.buildCFG(functionSyntax);
222+
cfg = trimFor(cfg);
223+
if (simplify) {
224+
cfg = simplifyCFG(cfg, mergeNodeAttrs);
225+
}
226+
cfg = remapNodeTargets(cfg);
227+
228+
savedCFG = cfg;
200229
}
201230

202-
const dot = graphToDot(cfg);
231+
const nodeToHighlight = getValue(cfg.offsetToNode, offset);
232+
const dot = graphToDot(cfg, false, nodeToHighlight);
203233
const svg = graphviz.dot(dot);
204234

205235
provider.setSVG(svg);

0 commit comments

Comments
 (0)