-
Notifications
You must be signed in to change notification settings - Fork 5
Adding function information to the /render page #134
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Alon,
Thanks for your contribution!
I went over it, and there are a few things that need more work.
There's a bit of code to change and move around, and tests that need to be written.
Additionally, I think there are visual-design changes required:
- The current color is shared for both the dark and light themes, and only kinda works with either. I think it is better to use different colors for the different themes. This is especially important on the light theme, that currently has very low contrast of text and background.
- As this is a programming tool, and we're presenting data, I think a monospaced font will be a better fit.
- There is currently a lot of spacing in the display. Between the lines and between the labels and the info. I think it should be tightened.
- I think the text should be selectable. There is no reason to prevent selection.
- I think it'll be better for the data to be presented under the controls and not on the opposite side
- Maybe it's worth it to add the project and filepath as well as the function name?
If you have any disagreement regarding the visual side - please let me know!
Last but not least - please make sure you run the linters (bun lint
) and they pass before submitting your code. Note that this is critical, and in most cases I would not review code that does not pass the automated checks.
Hey Tamir! First of all, thank you for reviewing the code! Regarding linting: Secondly, in Regarding the visual aspects - any preferences you have will be applied Of course, all your comments will be addressed, fixed, and modified accordingly. Thanks again for the review! changes are on the way :) |
The linters passing for all files are required for merging the code, you can see the failing checks at https://github.com/tmr232/function-graph-overview/actions/runs/13949594615/job/39061318568?pr=134
That's my bad - I changed the command from |
extracted the functions and changed app.svelte still working on fetching once in render func.
…pport for additional node types checked with github urls
# Conflicts: # src/control-flow/function-utils.ts
…oads and destructors Added tests for these cases. bun test: added snapshots (is that ok?)
…ort variable declarations
Hey Tamir, I saw that you changed the test framework from bun:test to vitest, and updated only the src/parser-loader/bun.ts file — but not src/parser-loader/vite.ts. I wanted to ask if you're planning to update vite.ts as well, because in src/render/src/App.svelte we’re importing initParsers and iterFunctions from file-parsing/vite.ts. In our tests, we also import from file-parsing/vite.ts, and it’s currently failing because of the wasm path issue. When we import from file-parsing/bun.ts, it works. Just wanted to check if you're planning to handle this too. Thanks! |
I guess the recent changes make the naming less clear... |
Used a direct Tree-sitter query Still need to figure out a way to do the same for Go , I couldn't find a generic approach yet.
# Conflicts: # src/test/functionDetails.test.ts
…pdate usages test: add test for function expression with direct name in TypeScript
…ach (except for C++) fix: Go name extraction now works correctly and without bugs all name extraction logic is now working well
Updated `updateMetadata` to support new logic We should note that I'm still fetching twice from GitHub..
Hi Tamir, Here’s what we’ve done so far: First, we added support for displaying both the CFG metadata and the function name in GitHub, as well as the CFG metadata in the Graph. We used Tree-sitter queries to improve the extraction process (thanks for the suggestion). The code for extracting function names is spread across We also kept About conflicts in function names: We added 35 tests for all the scenarios we came up with, covering different languages and function types. On the frontend side: (Just a note: there’s currently nothing in the README that explains how to use Graph on the render page) When the type is GitHub, we’re fetching the data twice... |
Also, when I commit my changes, should I also include the snapshot updates that were generated by the tests? |
Are you seeing any changes beyond line-ending changes in the snapshots? |
Oh, this is weird. Earlier this week, I saw a lot of changes in the snapshot files, but now, when I run the tests, it only changes the end-of-line characters, as you mentioned. But for now, I guess it's alright. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only reviewed the name extraction logic, not the UI part.
There are some questions, but also some bugs. I'm pusing a couple of tests that demonstrate them.
@@ -166,3 +166,17 @@ function processSwitchlike(switchSyntax: SyntaxNode, ctx: Context): BasicBlock { | |||
|
|||
return blockHandler.update({ entry: headNode, exit: mergeNode }); | |||
} | |||
|
|||
const nodeType = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this constant for? Why not use the names directlu?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll modify it
|
||
export function extractCFunctionName(func: SyntaxNode): string | undefined { | ||
if (func.type === nodeType.functionDefinition) { | ||
return func.descendantsOfType(nodeType.identifier)[0]?.text; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a case where this really is undefined, or is it due to the weird typing in the current web-tree-sitter library? If it's the weird types, we have treeSitterNoNullNodes
to deal with that in an "easy to find later" way.
tag: string, | ||
): string | undefined { | ||
const language = func.tree.language; | ||
const queryObj = new Query(language, query); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure we can accidentally get a nested function here.
Also, there's an entire query mechanism that we can use.
func: SyntaxNode, | ||
language: Language, | ||
): string | undefined { | ||
switch (language) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to use a mapping to easily enforce all languages via typing.
@@ -0,0 +1,83 @@ | |||
import { Query, type Node as SyntaxNode } from "web-tree-sitter"; | |||
import type { Language } from "./cfg"; | |||
import { extractCFunctionName } from "./cfg-c.ts"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This leads to a cyclic dependency. Honestly - I'm surprised this works at all. In any case - it's better not to have cycles.
tag: "var.name", | ||
}; | ||
|
||
const assignmentQueryAndTag = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All three queries can be unified with an alternation in the query.
return extractNameByNodeType(func, nodeType.fieldIdentifier); | ||
case nodeType.funcLiteral: | ||
// Check if the func_literal is assigned to a variable or is a standalone function | ||
return ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is important to limit the depth of a query, so that they properly ignore nesting.
Consider:
func main() {
var x = func() {
y := func() {}
y()
}
x()
}
See maxStartDepth
(used in block-matcher.ts
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I'll check it out and make the changes.
|
||
const variableDeclaratorQueryAndTag = { | ||
query: ` | ||
(lexical_declaration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What of var
definitions, and of definitions without either var
, let
or const
?
* | ||
* @param func - The syntax node to search within. | ||
* @param type - The type of the child node to extract the name from. | ||
* used among all languages (mostly the easy cases of extracting the name). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line currently renders as part of the parameter definition, which seems odd to me.
.map((c) => c.node.text); | ||
|
||
if (names.length > 1) { | ||
return "<unsupported>"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are we actually checking here? What is not supported, and why are we treating it differently than other cases where we failed to get a name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we discussed before, when I have something like y, x := func() {}, func() {}
, the query for extracting the variables returns two results:
(short_var_declaration left: (expression_list (identifier) @name))
It's unclear what the "name" of the function is, because there are two expressions. Since I get two matches for one query, I treat it as a conflict.
How can I handle this better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First thing, adding a comment that says this is helpful.
Second thing - please be consistent. There are more cases where we can't get the name of the function. Why are some just unnamed, and this is <unsupported>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right , I should have included an explanation in the code itself.
Regarding the second thing:
I understand what you're saying. I thought it was different because the extraction succeeded, it just returned more than one name.
In most other cases, I only got a single name, so I could tell if the name extraction had failed.
I could return undefined instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I just return undefined in this case and treat it as an unnamed function?
I think that makes sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, undefined
if we can't get the name, the name if we can, anonymous
if there isn't one.
Hi , So, you’ve added a couple of tests and I’d like to talk about one of them
If you ask me what the name of this function is, I’d probably say "main"
Since we always want to get the outer function name for any link and line number everything works. we would get x. To be completely honest, I didn’t really understand the test. (The test is in Go section) |
That may be true, but what the test is asking is "what are the names of all the functions here". If we have a function that extracts function names, there is no reason for it to not work on nested functions. The test is there because in that code, I expect to be able to query the names of all the functions, and get correct results (or Additionally, the test does not have to match the |
This is kind of a new feature, extracting function names from within a function. Let’s say I have a solution that returns a list of names: I'll keep in touch |
const language = func.tree.language; | ||
const queryObj = new Query(language, query); | ||
|
||
const rootNode = func.tree.rootNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I somehow missed this... You're getting the root of the file - this will never work.
Try the following:
If you print all the names you get with this, you'll see what I mean.
There's no need to get a list of names. You need to get the correct name for the function object. Also, for the render page, it is not always the "outer name", it's the name of the function on the line we choose. |
Hey Tamir!
We've added function info to the render page - it updates only when fetching from GitHub (via URL).
Currently, most of the code is in App.svelte.
Here are some example links to test locally (port 5173):
1
2
3
4
5
6
7
8
We are waiting for your feedback and hopefully it meets your standards.