Skip to content

Commit 1a3cad8

Browse files
authored
Merge pull request #13 from tmr232/extension-highlight-nodes
Highlight Current Node
2 parents e989a09 + c4df9f8 commit 1a3cad8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+9031
-709
lines changed

.vscode/extensions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
"ms-vscode.extension-test-runner",
88
"oven.bun-vscode"
99
]
10-
}
10+
}

CHANGELOG.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,29 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- The CFG view now highlights (in black) the node matching the cursor position.
12+
- Basic CFG caching for tests, making them twice as fast.
13+
- The extension learned to only generate a CFG on code or config changes.
14+
If the cursor just moves inside the same function, we don't regenerate the CFG.
15+
916
### Fixed
1017

1118
- Rendering of `select` blocks in Go was broken.
19+
- Empty case clauses in `switch` statements no longer cause crashes.
20+
- Last case of a Python `match` statement no longer assumed to match.
1221

1322
### Changed
1423

1524
- Massive refactoring of `CFGBuilder` classes.
16-
New design now uses the same`GenericCGBuilder` class
17-
for all languages, and takes statement handlers as
18-
arguments.
25+
New design now uses the same`GenericCFGBuilder` class for all languages,
26+
and takes statement handlers as arguments.
1927
This reduces code duplication and makes it easier to add
2028
new languages in the future.
29+
- Flat switches now generate nodes for the conditions, and not only the consequence.
30+
- The CodeMirror editor in the demo got it's own Svelte component now, `Editor.svelte`.
31+
This allows better state management and handling/dispatching events.
2132

2233
## [0.0.5] - 2024-09-18
2334

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@
8585
"type": "boolean",
8686
"default": "false",
8787
"description": "Flatten switches, so that all cases are direct descendants of the root."
88+
},
89+
"functionGraphOverview.highlightCurrentNode": {
90+
"type": "boolean",
91+
"default": "true",
92+
"description": "Highlight the CFG node matching the current code cursor position."
8893
}
8994
}
9095
},

src/control-flow/block-matcher.ts

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
import Parser from "web-tree-sitter";
22
import { type BasicBlock, BlockHandler } from "./cfg-defs.ts";
3+
import { evolve } from "./evolve.ts";
4+
5+
const defaultQueryOptions: Parser.QueryOptions = { maxStartDepth: 0 };
36

47
function matchQuery(
58
syntax: Parser.SyntaxNode,
69
queryString: string,
10+
options?: Parser.QueryOptions,
711
): Parser.QueryMatch {
812
const language = syntax.tree.getLanguage();
913
const query = language.query(queryString);
10-
const matches = query.matches(syntax, { maxStartDepth: 0 });
14+
options = evolve(defaultQueryOptions, options ?? {});
15+
const matches = query.matches(syntax, options);
16+
1117
if (matches.length === 0) {
1218
throw new Error(`No match found for query.`);
1319
}
20+
// @ts-expect-error: tsc can't deduce that an element must exist.
1421
return matches[0];
1522
}
1623

1724
export function matchExistsIn(
1825
syntax: Parser.SyntaxNode,
19-
mainName: string,
2026
queryString: string,
2127
): boolean {
2228
const language = syntax.tree.getLanguage();
@@ -32,6 +38,14 @@ function getSyntax(
3238
return getSyntaxMany(match, name)[0];
3339
}
3440

41+
function getLastSyntax(
42+
match: Parser.QueryMatch,
43+
name: string,
44+
): Parser.SyntaxNode | undefined {
45+
const many = getSyntaxMany(match, name);
46+
return many[many.length - 1];
47+
}
48+
3549
function requireSyntax(
3650
match: Parser.QueryMatch,
3751
name: string,
@@ -54,16 +68,20 @@ function getSyntaxMany(
5468

5569
export class BlockMatcher {
5670
private blockHandler: BlockHandler = new BlockHandler();
57-
private processBlock: (syntax: Parser.SyntaxNode | null) => BasicBlock;
71+
private dispatchSingle: (syntax: Parser.SyntaxNode | null) => BasicBlock;
5872
public update = this.blockHandler.update.bind(this.blockHandler);
5973

60-
constructor(processBlock: BlockMatcher["processBlock"]) {
61-
this.processBlock = processBlock;
74+
constructor(dispatchSingle: BlockMatcher["dispatchSingle"]) {
75+
this.dispatchSingle = dispatchSingle;
6276
}
6377

64-
public match(syntax: Parser.SyntaxNode, queryString: string): Match {
65-
const match = matchQuery(syntax, queryString);
66-
return new Match(match, this.blockHandler, this.processBlock);
78+
public match(
79+
syntax: Parser.SyntaxNode,
80+
queryString: string,
81+
options?: Parser.QueryOptions,
82+
): Match {
83+
const match = matchQuery(syntax, queryString, options);
84+
return new Match(match, this.blockHandler, this.dispatchSingle);
6785
}
6886

6987
public tryMatch(
@@ -82,32 +100,57 @@ export class BlockMatcher {
82100
}
83101
}
84102

103+
/**
104+
* Maintains a single {Parser.QueryMatch} and allows accesing the captures within it.
105+
*/
85106
export class Match {
86107
private match: Parser.QueryMatch;
87108
private blockHandler: BlockHandler;
88-
private processBlock: BlockMatcher["processBlock"];
109+
private dispatchSingle: BlockMatcher["dispatchSingle"];
89110
constructor(
90111
match: Parser.QueryMatch,
91112
blockHandler: BlockHandler,
92-
processBlock: BlockMatcher["processBlock"],
113+
dispatchSingle: BlockMatcher["dispatchSingle"],
93114
) {
94115
this.match = match;
95116
this.blockHandler = blockHandler;
96-
this.processBlock = processBlock;
117+
this.dispatchSingle = dispatchSingle;
97118
}
98119

120+
/**
121+
* Get the first named syntax node from the query match.
122+
* @param name Name of the capture
123+
* @returns {Parser.SyntaxNode|undefined} The syntax matching the capture name, if captured.
124+
*/
99125
public getSyntax(name: string): ReturnType<typeof getSyntax> {
100126
return getSyntax(this.match, name);
101127
}
102128

129+
public getLastSyntax(name: string): ReturnType<typeof getLastSyntax> {
130+
return getLastSyntax(this.match, name);
131+
}
132+
103133
public requireSyntax(name: string): ReturnType<typeof requireSyntax> {
104134
return requireSyntax(this.match, name);
105135
}
106136

107137
public getSyntaxMany(name: string): ReturnType<typeof getSyntaxMany> {
108138
return getSyntaxMany(this.match, name);
109139
}
110-
public getBlock(syntax: Parser.SyntaxNode | null | undefined) {
111-
return syntax ? this.blockHandler.update(this.processBlock(syntax)) : null;
140+
141+
public getBlock(syntax: Parser.SyntaxNode): BasicBlock;
142+
public getBlock(
143+
syntax: Parser.SyntaxNode | null | undefined,
144+
): BasicBlock | null;
145+
public getBlock(
146+
syntax: Parser.SyntaxNode | null | undefined,
147+
): BasicBlock | null {
148+
return syntax
149+
? this.blockHandler.update(this.dispatchSingle(syntax))
150+
: null;
151+
}
152+
153+
public getManyBlocks(syntaxMany: Parser.SyntaxNode[]): BasicBlock[] {
154+
return syntaxMany.map((syntax) => this.getBlock(syntax) as BasicBlock);
112155
}
113156
}

src/control-flow/builder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export class Builder {
5252
lines,
5353
markers: [],
5454
cluster,
55+
targets: [id],
5556
});
5657
return id;
5758
}

0 commit comments

Comments
 (0)