Skip to content
This repository was archived by the owner on Mar 10, 2026. It is now read-only.

Commit 3ad7900

Browse files
gkorlandCopilot
andcommitted
Merge main into JS support branch and rewrite analyzer
Resolve merge conflicts with main branch which restructured the analyzer pattern. Rewrite JavaScriptAnalyzer to follow the new AbstractAnalyzer interface with get_entity_label, get_entity_name, get_entity_docstring, get_entity_types, add_symbols, resolve_symbol, and other required abstract methods. Address PR review comments: - Replace wildcard imports with explicit imports (CodeRabbit) - Move JS-specific utils out of shared utils.py (swilly22) - Remove redundant file re-parsing in second pass (swilly22) - Replace assert with proper error handling pattern (copilot-reviewer) - Improve docstrings to be more specific (swilly22) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2 parents 754f412 + 4fefd9f commit 3ad7900

51 files changed

Lines changed: 4582 additions & 6307 deletions

Some content is hidden

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

.github/dependabot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ updates:
44
directory: "/" # Location of package manifests
55
schedule:
66
interval: "daily"
7-
target-branch: "staging"
7+
target-branch: "staging"

.github/workflows/python-app.yml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ name: Python application
55

66
on:
77
push:
8-
branches: [ "main" ]
8+
branches: [ "main", "staging" ]
99
pull_request:
10-
branches: [ "main" ]
10+
branches: [ "main", "staging" ]
1111

1212
permissions:
1313
contents: read
@@ -19,15 +19,16 @@ jobs:
1919

2020
steps:
2121
- uses: actions/checkout@v4
22-
- name: Set up Python 3.10
23-
uses: actions/setup-python@v3
22+
- name: Install uv
23+
uses: astral-sh/setup-uv@v4
24+
- name: Set up Python 3.12
25+
uses: actions/setup-python@v5
2426
with:
25-
python-version: "3.10"
27+
python-version: "3.12"
2628
- name: Install dependencies
2729
run: |
28-
python -m pip install --upgrade pip
29-
pip install flake8 pytest
30-
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
30+
uv pip install --system flake8 pytest
31+
uv pip install --system .
3132
- name: Lint with flake8
3233
run: |
3334
# stop the build if there are Python syntax errors or undefined names
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Release image to DockerHub
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags: ["v*.*.*"]
7+
branches:
8+
- main
9+
10+
jobs:
11+
build-and-release:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Set tags
18+
run: |
19+
if ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags') }}; then
20+
echo "TAGS=falkordb/code-graph-backend:latest,falkordb/code-graph-backend:${{ github.ref_name }}" >> $GITHUB_ENV
21+
else
22+
echo "TAGS=falkordb/code-graph-backend:edge" >> $GITHUB_ENV
23+
fi
24+
25+
- name: Login to DockerHub
26+
uses: docker/login-action@v3
27+
with:
28+
username: ${{ secrets.DOCKER_USERNAME }}
29+
password: ${{ secrets.DOCKER_PASSWORD }}
30+
31+
- name: Build image
32+
uses: docker/build-push-action@v5
33+
with:
34+
context: .
35+
file: ./Dockerfile
36+
push: true
37+
tags: ${{ env.TAGS }}

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,17 @@ docker run -p 6379:6379 -p 3000:3000 -it --rm falkordb/falkordb:latest
2222

2323
Create your own `.env` file from the `.env.template` file
2424

25+
### Install dependencies
26+
27+
Install project dependencies:
28+
29+
```bash
30+
uv sync
31+
```
32+
2533
Start the server:
2634
```bash
27-
flask --app api/index.py run --debug
35+
uv run flask --app api/index.py run --debug
2836
```
2937

3038
### Creating a graph
@@ -48,7 +56,7 @@ a graph representation of your source code, the graph name should be the same as
4856
the name of the folder you've requested to analyze, for the example above a graph named:
4957
"GraphRAG-SDK".
5058

51-
At the moment only the Python and C languages are supported, we do intend to support additional languages.
59+
At the moment Python, Java, and C# languages are supported, we do intend to support additional languages.
5260

5361
At this point you can explore and query your source code using various tools
5462
Here are several options:

api/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from .info import *
22
from .llm import ask
33
from .graph import *
4+
from .project import *
45
from .entities import *
6+
from .git_utils import *
57
from .code_coverage import *
68
from .analyzers.source_analyzer import *
79
from .auto_complete import prefix_search

api/analyzers/analyzer.py

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,144 @@
1-
import io
21
from pathlib import Path
2+
from typing import Optional
3+
4+
from tree_sitter import Language, Node, Parser, Point
5+
from api.entities.entity import Entity
6+
from api.entities.file import File
37
from abc import ABC, abstractmethod
8+
from multilspy import SyncLanguageServer
49

510
class AbstractAnalyzer(ABC):
11+
def __init__(self, language: Language) -> None:
12+
self.language = language
13+
self.parser = Parser(language)
14+
15+
def find_parent(self, node: Node, parent_types: list) -> Node:
16+
while node and node.type not in parent_types:
17+
node = node.parent
18+
return node
19+
20+
@abstractmethod
21+
def is_dependency(self, file_path: str) -> bool:
22+
"""
23+
Check if the file is a dependency.
24+
25+
Args:
26+
file_path (str): The file path.
27+
28+
Returns:
29+
bool: True if the file is a dependency, False otherwise.
30+
"""
31+
32+
pass
33+
34+
@abstractmethod
35+
def resolve_path(self, file_path: str, path: Path) -> str:
36+
"""
37+
Resolve the path of the file.
38+
39+
Args:
40+
file_path (str): The file path.
41+
path (Path): The path to the folder.
42+
43+
Returns:
44+
str: The resolved path.
45+
"""
46+
47+
pass
48+
49+
def resolve(self, files: dict[Path, File], lsp: SyncLanguageServer, file_path: Path, path: Path, node: Node) -> list[tuple[File, Node]]:
50+
try:
51+
locations = lsp.request_definition(str(file_path), node.start_point.row, node.start_point.column)
52+
return [(files[Path(self.resolve_path(location['absolutePath'], path))], files[Path(self.resolve_path(location['absolutePath'], path))].tree.root_node.descendant_for_point_range(Point(location['range']['start']['line'], location['range']['start']['character']), Point(location['range']['end']['line'], location['range']['end']['character']))) for location in locations if location and Path(self.resolve_path(location['absolutePath'], path)) in files]
53+
except Exception as e:
54+
return []
55+
656
@abstractmethod
7-
def first_pass(self, path: Path, f: io.TextIOWrapper) -> None:
57+
def add_dependencies(self, path: Path, files: list[Path]):
858
"""
9-
Perform the first pass of analysis on the given file.
59+
Add dependencies to the files.
1060
1161
Args:
12-
path (Path): The path to the file being processed.
13-
f (io.TextIOWrapper): The file object.
62+
path (Path): The path to the folder.
63+
files (dict[Path, File]): The files.
64+
"""
65+
66+
pass
67+
68+
@abstractmethod
69+
def get_entity_label(self, node: Node) -> str:
1470
"""
71+
Get the entity label from the node.
1572
73+
Args:
74+
node (Node): The node.
75+
76+
Returns:
77+
str: The entity label.
78+
"""
1679
pass
1780

1881
@abstractmethod
19-
def second_pass(self, path: Path, f: io.TextIOWrapper) -> None:
82+
def get_entity_name(self, node: Node) -> str:
2083
"""
21-
Perform a second pass analysis on the given source file.
84+
Get the entity name from the node.
2285
2386
Args:
87+
node (Node): The node.
88+
89+
Returns:
90+
str: The entity name.
91+
"""
92+
pass
93+
94+
@abstractmethod
95+
def get_entity_docstring(self, node: Node) -> Optional[str]:
96+
"""
97+
Get the entity docstring from the node.
98+
99+
Args:
100+
node (Node): The node.
101+
102+
Returns:
103+
Optional[str]: The entity docstring.
104+
"""
105+
pass
106+
107+
@abstractmethod
108+
def get_entity_types(self) -> list[str]:
109+
"""
110+
Get the top level entity types for the language.
111+
112+
Returns:
113+
list[str]: The list of top level entity types.
114+
"""
115+
116+
pass
117+
118+
@abstractmethod
119+
def add_symbols(self, entity: Entity) -> None:
120+
"""
121+
Add symbols to the entity.
122+
123+
Args:
124+
entity (Entity): The entity to add symbols to.
125+
"""
126+
127+
pass
128+
129+
@abstractmethod
130+
def resolve_symbol(self, files: dict[Path, File], lsp: SyncLanguageServer, file_path: Path, path: Path, key: str, symbol: Node) -> list[Entity]:
131+
"""
132+
Resolve a symbol to an entity.
133+
134+
Args:
135+
lsp (SyncLanguageServer): The language server.
24136
path (Path): The path to the file.
25-
f (io.TextIOWrapper): The file handle of the file to be processed.
137+
key (str): The symbol key.
138+
symbol (Node): The symbol node.
139+
140+
Returns:
141+
list[Entity]: The resolved entities.
26142
"""
27143

28144
pass

0 commit comments

Comments
 (0)