Skip to content

Highlight Current Node #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 86 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
2e4c169
Allow first-line numbers in nodes, and merge them properly
tmr232 Sep 15, 2024
0158c54
Hacky and half-way there.
tmr232 Sep 15, 2024
ea1120c
Now it actually works. Hacky as hell, but I get a go-to-line for Python!
tmr232 Sep 15, 2024
7031230
Simplify the node-onclick handling code
tmr232 Sep 17, 2024
fde2b6c
Merge branch 'main' into jump-to-line
tmr232 Sep 19, 2024
ad56468
POC for marking current node
tmr232 Sep 19, 2024
baba71f
Move editor functionality to a dedicated svelte component
tmr232 Sep 19, 2024
cc93484
Two-way line-to-node mapping working in the demo!
tmr232 Sep 19, 2024
78f5d2c
Added missing file
tmr232 Sep 19, 2024
bc87a0a
Merge branch 'navigation' into jump-to-line
tmr232 Sep 21, 2024
eaeefc1
Initial mapping support!
tmr232 Sep 21, 2024
6b54b99
Fixed some mapping issues
tmr232 Sep 21, 2024
4de9c70
Fixed some mapping issues
tmr232 Sep 21, 2024
baa669b
Improved node-syntax mapping in Go
tmr232 Sep 21, 2024
98cd9ba
Add basic ranges implementation
tmr232 Sep 21, 2024
9d0ad00
Range implementation
tmr232 Sep 22, 2024
5e97dbd
Ranges seem to work, but mapping is off
tmr232 Sep 22, 2024
ee96983
Minor mapping improvements. But it's getting clear that we need bette…
tmr232 Sep 22, 2024
ad74e90
Added basic visualization for source-to-node mapping
tmr232 Sep 23, 2024
c29f8b4
Some mapping improvements, and more ideas!
tmr232 Sep 23, 2024
f4a3da7
Many more improvements!
tmr232 Sep 23, 2024
b47adb0
More improvements to if-elif-else mapping in Python
tmr232 Sep 24, 2024
eb4213c
Added point-based ranges (row,col)
tmr232 Sep 24, 2024
0f339ec
Working range implementation with row-col pairs instead of offsets
tmr232 Sep 24, 2024
32dabc3
Formatting
tmr232 Sep 24, 2024
8cde8fe
This looks much nicer!
tmr232 Sep 24, 2024
0d4b8a9
Add zip utilities
tmr232 Sep 24, 2024
795020a
Minor changes
tmr232 Sep 24, 2024
4f38c0a
Started using zip
tmr232 Sep 24, 2024
3ba1da2
Use more zip, and add a pairwise generator
tmr232 Sep 24, 2024
85671a5
Map Python's switch and for
tmr232 Sep 24, 2024
3daa5d9
Better typing!
tmr232 Sep 24, 2024
a83e1fd
Simplified some code
tmr232 Sep 24, 2024
3f0654e
Formatting
tmr232 Sep 24, 2024
aafe6df
Cleanup
tmr232 Sep 24, 2024
406d925
Rename dispatch methods to make them more obvious to the reader
tmr232 Sep 24, 2024
18170fb
Fix a bug in if-statement node mapping
tmr232 Sep 24, 2024
7b05fd9
Learned to map try-complex and with-statement
tmr232 Sep 25, 2024
ca10c2c
remove unnecessary checks
tmr232 Sep 25, 2024
b5c1ac2
Learned to map match-statement
tmr232 Sep 25, 2024
5c4a0d1
Formatting
tmr232 Sep 25, 2024
581244e
Update test results
tmr232 Sep 25, 2024
0f96bbc
End-of-line mapps to one character before
tmr232 Sep 25, 2024
031a81f
de-verbosify default output
tmr232 Sep 25, 2024
9575153
cfg-go: learned to map if-statements to nodes
tmr232 Sep 25, 2024
e5f4a29
cfg-go: learned to map non-flat switch
tmr232 Sep 25, 2024
ecdd720
cfg-go: improved flat-switch construction
tmr232 Sep 25, 2024
1d35115
cfg-go: learned to map flat-switch
tmr232 Sep 25, 2024
2ddd133
cfg-go: last case now extends to end-of-switch
tmr232 Sep 25, 2024
52071f7
cfg-go: Fixed a bug where empty-cases would cause a crash
tmr232 Sep 25, 2024
cb839dc
Format and lint
tmr232 Sep 25, 2024
1948991
Format and lint
tmr232 Sep 25, 2024
f9451bf
cfg-c: learned node mapping for most statements
tmr232 Sep 25, 2024
8f21aae
tsc: Solve some indexing issues
tmr232 Sep 25, 2024
f314a4e
tsc: Solve some indexing issues
tmr232 Sep 25, 2024
5b906bf
tsc: Stricter typing enforced
tmr232 Sep 25, 2024
e297b7f
tsc: enforced stricter settings
tmr232 Sep 25, 2024
ceef136
Remove unused code
tmr232 Sep 25, 2024
43e89cc
cfg-c: learned to map if-else code to nodes
tmr232 Sep 25, 2024
444b7cc
cfg-c: Better mapping of for and if
tmr232 Sep 25, 2024
89d5be6
Added snapshot tests for offset-to-node mapping
tmr232 Sep 25, 2024
f8a897e
cfg-c: Learned to map for loops
tmr232 Sep 25, 2024
c4e5a0d
cfg-c, cfg-go: reordered functions to make them more similar
tmr232 Sep 25, 2024
ddbd787
cfg-c: learned to map switch
tmr232 Sep 25, 2024
b4de693
cfg-c, cfg-go: Generalized switch-handling code to handle both languages
tmr232 Sep 25, 2024
d2bb8f5
Docs!
tmr232 Sep 25, 2024
71b3573
Cleaner code-to-node link interface
tmr232 Sep 25, 2024
c292a55
Remove unused code
tmr232 Sep 25, 2024
acc35d9
tests: Speed up using caching
tmr232 Sep 25, 2024
61cae31
Move node highlighting to a function
tmr232 Sep 26, 2024
8ca7ccb
Slightly better event handling in the demo
tmr232 Sep 26, 2024
31d5a4d
generic-cfg-builder: the end of a function is now mapped to the last …
tmr232 Sep 26, 2024
3f01cb3
Update test snapshots
tmr232 Sep 26, 2024
1a289b5
vscode: General cleanup in the extension code
tmr232 Sep 26, 2024
6741588
vscode: Learned to highlight the current node
tmr232 Sep 26, 2024
ada32a2
test: Normalize line-endings for segmentation snapshots
tmr232 Sep 26, 2024
71efdc8
vscode: Settings allow disabling node highlighting
tmr232 Sep 26, 2024
600f8e6
Important todo note
tmr232 Sep 26, 2024
4ea5d17
Demo: Remove CSS-based highlighting in favor of DOT highlighting.
tmr232 Sep 26, 2024
83f66a6
CFG: Remove unused code for mapping nodes to source lines
tmr232 Sep 26, 2024
145d14e
Removed debug prints
tmr232 Sep 26, 2024
1625730
Changelog and docs
tmr232 Sep 26, 2024
ccc2c52
generic-cfg-builder: Move the statement handlers into this file
tmr232 Sep 26, 2024
083fb6c
Remove unused code
tmr232 Sep 26, 2024
23f50ad
Remove unused code
tmr232 Sep 26, 2024
c4df9f8
Minor changes
tmr232 Sep 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
"ms-vscode.extension-test-runner",
"oven.bun-vscode"
]
}
}
17 changes: 14 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,29 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how

## [Unreleased]

### Added

- The CFG view now highlights (in black) the node matching the cursor position.
- Basic CFG caching for tests, making them twice as fast.
- The extension learned to only generate a CFG on code or config changes.
If the cursor just moves inside the same function, we don't regenerate the CFG.

### Fixed

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

### Changed

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

## [0.0.5] - 2024-09-18

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@
"type": "boolean",
"default": "false",
"description": "Flatten switches, so that all cases are direct descendants of the root."
},
"functionGraphOverview.highlightCurrentNode": {
"type": "boolean",
"default": "true",
"description": "Highlight the CFG node matching the current code cursor position."
}
}
},
Expand Down
69 changes: 56 additions & 13 deletions src/control-flow/block-matcher.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import Parser from "web-tree-sitter";
import { type BasicBlock, BlockHandler } from "./cfg-defs.ts";
import { evolve } from "./evolve.ts";

const defaultQueryOptions: Parser.QueryOptions = { maxStartDepth: 0 };

function matchQuery(
syntax: Parser.SyntaxNode,
queryString: string,
options?: Parser.QueryOptions,
): Parser.QueryMatch {
const language = syntax.tree.getLanguage();
const query = language.query(queryString);
const matches = query.matches(syntax, { maxStartDepth: 0 });
options = evolve(defaultQueryOptions, options ?? {});
const matches = query.matches(syntax, options);

if (matches.length === 0) {
throw new Error(`No match found for query.`);
}
// @ts-expect-error: tsc can't deduce that an element must exist.
return matches[0];
}

export function matchExistsIn(
syntax: Parser.SyntaxNode,
mainName: string,
queryString: string,
): boolean {
const language = syntax.tree.getLanguage();
Expand All @@ -32,6 +38,14 @@ function getSyntax(
return getSyntaxMany(match, name)[0];
}

function getLastSyntax(
match: Parser.QueryMatch,
name: string,
): Parser.SyntaxNode | undefined {
const many = getSyntaxMany(match, name);
return many[many.length - 1];
}

function requireSyntax(
match: Parser.QueryMatch,
name: string,
Expand All @@ -54,16 +68,20 @@ function getSyntaxMany(

export class BlockMatcher {
private blockHandler: BlockHandler = new BlockHandler();
private processBlock: (syntax: Parser.SyntaxNode | null) => BasicBlock;
private dispatchSingle: (syntax: Parser.SyntaxNode | null) => BasicBlock;
public update = this.blockHandler.update.bind(this.blockHandler);

constructor(processBlock: BlockMatcher["processBlock"]) {
this.processBlock = processBlock;
constructor(dispatchSingle: BlockMatcher["dispatchSingle"]) {
this.dispatchSingle = dispatchSingle;
}

public match(syntax: Parser.SyntaxNode, queryString: string): Match {
const match = matchQuery(syntax, queryString);
return new Match(match, this.blockHandler, this.processBlock);
public match(
syntax: Parser.SyntaxNode,
queryString: string,
options?: Parser.QueryOptions,
): Match {
const match = matchQuery(syntax, queryString, options);
return new Match(match, this.blockHandler, this.dispatchSingle);
}

public tryMatch(
Expand All @@ -82,32 +100,57 @@ export class BlockMatcher {
}
}

/**
* Maintains a single {Parser.QueryMatch} and allows accesing the captures within it.
*/
export class Match {
private match: Parser.QueryMatch;
private blockHandler: BlockHandler;
private processBlock: BlockMatcher["processBlock"];
private dispatchSingle: BlockMatcher["dispatchSingle"];
constructor(
match: Parser.QueryMatch,
blockHandler: BlockHandler,
processBlock: BlockMatcher["processBlock"],
dispatchSingle: BlockMatcher["dispatchSingle"],
) {
this.match = match;
this.blockHandler = blockHandler;
this.processBlock = processBlock;
this.dispatchSingle = dispatchSingle;
}

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

public getLastSyntax(name: string): ReturnType<typeof getLastSyntax> {
return getLastSyntax(this.match, name);
}

public requireSyntax(name: string): ReturnType<typeof requireSyntax> {
return requireSyntax(this.match, name);
}

public getSyntaxMany(name: string): ReturnType<typeof getSyntaxMany> {
return getSyntaxMany(this.match, name);
}
public getBlock(syntax: Parser.SyntaxNode | null | undefined) {
return syntax ? this.blockHandler.update(this.processBlock(syntax)) : null;

public getBlock(syntax: Parser.SyntaxNode): BasicBlock;
public getBlock(
syntax: Parser.SyntaxNode | null | undefined,
): BasicBlock | null;
public getBlock(
syntax: Parser.SyntaxNode | null | undefined,
): BasicBlock | null {
return syntax
? this.blockHandler.update(this.dispatchSingle(syntax))
: null;
}

public getManyBlocks(syntaxMany: Parser.SyntaxNode[]): BasicBlock[] {
return syntaxMany.map((syntax) => this.getBlock(syntax) as BasicBlock);
}
}
1 change: 1 addition & 0 deletions src/control-flow/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class Builder {
lines,
markers: [],
cluster,
targets: [id],
});
return id;
}
Expand Down
Loading