codegraphcontext-rust 0.5.5__cp311-cp311-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codegraphcontext/__init__.py +2 -0
- codegraphcontext/__main__.py +5 -0
- codegraphcontext/_cgc_rust.cp311-win_amd64.pyd +0 -0
- codegraphcontext/cli/__init__.py +1 -0
- codegraphcontext/cli/cli_helpers.py +774 -0
- codegraphcontext/cli/config_manager.py +1051 -0
- codegraphcontext/cli/main.py +2386 -0
- codegraphcontext/cli/registry_commands.py +404 -0
- codegraphcontext/cli/setup_macos.py +93 -0
- codegraphcontext/cli/setup_wizard.py +991 -0
- codegraphcontext/cli/visualizer.py +50 -0
- codegraphcontext/core/__init__.py +165 -0
- codegraphcontext/core/bundle_registry.py +181 -0
- codegraphcontext/core/cgc_bundle.py +858 -0
- codegraphcontext/core/cgcignore.py +118 -0
- codegraphcontext/core/database.py +273 -0
- codegraphcontext/core/database_falkordb.py +480 -0
- codegraphcontext/core/database_falkordb_remote.py +199 -0
- codegraphcontext/core/database_kuzu.py +636 -0
- codegraphcontext/core/falkor_worker.py +132 -0
- codegraphcontext/core/jobs.py +132 -0
- codegraphcontext/core/watcher.py +260 -0
- codegraphcontext/prompts.py +125 -0
- codegraphcontext/server.py +471 -0
- codegraphcontext/tool_definitions.py +220 -0
- codegraphcontext/tools/__init__.py +0 -0
- codegraphcontext/tools/advanced_language_query_tool.py +103 -0
- codegraphcontext/tools/code_finder.py +1160 -0
- codegraphcontext/tools/graph_builder.py +337 -0
- codegraphcontext/tools/handlers/analysis_handlers.py +114 -0
- codegraphcontext/tools/handlers/indexing_handlers.py +116 -0
- codegraphcontext/tools/handlers/management_handlers.py +339 -0
- codegraphcontext/tools/handlers/query_handlers.py +83 -0
- codegraphcontext/tools/handlers/watcher_handlers.py +83 -0
- codegraphcontext/tools/indexing/__init__.py +1 -0
- codegraphcontext/tools/indexing/constants.py +25 -0
- codegraphcontext/tools/indexing/discovery.py +64 -0
- codegraphcontext/tools/indexing/engine.py +109 -0
- codegraphcontext/tools/indexing/persistence/__init__.py +3 -0
- codegraphcontext/tools/indexing/persistence/parquet_loader.py +643 -0
- codegraphcontext/tools/indexing/persistence/writer.py +1114 -0
- codegraphcontext/tools/indexing/pipeline.py +171 -0
- codegraphcontext/tools/indexing/pre_scan.py +105 -0
- codegraphcontext/tools/indexing/resolution/__init__.py +8 -0
- codegraphcontext/tools/indexing/resolution/calls.py +253 -0
- codegraphcontext/tools/indexing/resolution/inheritance.py +91 -0
- codegraphcontext/tools/indexing/sanitize.py +41 -0
- codegraphcontext/tools/indexing/schema.py +79 -0
- codegraphcontext/tools/indexing/schema_contract.py +44 -0
- codegraphcontext/tools/indexing/scip_pipeline.py +140 -0
- codegraphcontext/tools/languages/c.py +563 -0
- codegraphcontext/tools/languages/cpp.py +616 -0
- codegraphcontext/tools/languages/csharp.py +551 -0
- codegraphcontext/tools/languages/dart.py +378 -0
- codegraphcontext/tools/languages/elixir.py +460 -0
- codegraphcontext/tools/languages/go.py +499 -0
- codegraphcontext/tools/languages/haskell.py +426 -0
- codegraphcontext/tools/languages/java.py +500 -0
- codegraphcontext/tools/languages/javascript.py +589 -0
- codegraphcontext/tools/languages/kotlin.py +640 -0
- codegraphcontext/tools/languages/perl.py +260 -0
- codegraphcontext/tools/languages/php.py +519 -0
- codegraphcontext/tools/languages/python.py +576 -0
- codegraphcontext/tools/languages/ruby.py +537 -0
- codegraphcontext/tools/languages/rust.py +295 -0
- codegraphcontext/tools/languages/scala.py +519 -0
- codegraphcontext/tools/languages/swift.py +491 -0
- codegraphcontext/tools/languages/typescript.py +575 -0
- codegraphcontext/tools/languages/typescriptjsx.py +151 -0
- codegraphcontext/tools/package_resolver.py +472 -0
- codegraphcontext/tools/query_tool_languages/c_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +87 -0
- codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/dart_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/go_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/java_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/perl_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/python_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/rust_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/scala_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/swift_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +5 -0
- codegraphcontext/tools/scip_indexer.py +467 -0
- codegraphcontext/tools/scip_pb2.py +2455 -0
- codegraphcontext/tools/system.py +133 -0
- codegraphcontext/tools/tree_sitter_parser.py +103 -0
- codegraphcontext/utils/debug_log.py +90 -0
- codegraphcontext/utils/path_ignore.py +55 -0
- codegraphcontext/utils/repo_path.py +27 -0
- codegraphcontext/utils/tree_sitter_manager.py +285 -0
- codegraphcontext/utils/visualize_graph.py +0 -0
- codegraphcontext/viz/server.py +282 -0
- codegraphcontext_rust-0.5.5.dist-info/METADATA +537 -0
- codegraphcontext_rust-0.5.5.dist-info/RECORD +101 -0
- codegraphcontext_rust-0.5.5.dist-info/WHEEL +4 -0
- codegraphcontext_rust-0.5.5.dist-info/entry_points.txt +3 -0
- codegraphcontext_rust-0.5.5.dist-info/licenses/LICENSE +21 -0
- codegraphcontext_rust-0.5.5.dist-info/sboms/cgc-python.cyclonedx.json +2333 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Dict
|
|
3
|
+
from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger, debug_logger
|
|
4
|
+
from codegraphcontext.utils.tree_sitter_manager import execute_query
|
|
5
|
+
|
|
6
|
+
TS_QUERIES = {
|
|
7
|
+
"functions": """
|
|
8
|
+
(function_declaration
|
|
9
|
+
name: (identifier) @name
|
|
10
|
+
parameters: (formal_parameters) @params
|
|
11
|
+
) @function_node
|
|
12
|
+
|
|
13
|
+
(variable_declarator
|
|
14
|
+
name: (identifier) @name
|
|
15
|
+
value: (function_expression
|
|
16
|
+
parameters: (formal_parameters) @params
|
|
17
|
+
) @function_node
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
(variable_declarator
|
|
21
|
+
name: (identifier) @name
|
|
22
|
+
value: (arrow_function
|
|
23
|
+
parameters: (formal_parameters) @params
|
|
24
|
+
) @function_node
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
(variable_declarator
|
|
28
|
+
name: (identifier) @name
|
|
29
|
+
value: (arrow_function
|
|
30
|
+
parameter: (identifier) @single_param
|
|
31
|
+
) @function_node
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
(method_definition
|
|
35
|
+
name: (property_identifier) @name
|
|
36
|
+
parameters: (formal_parameters) @params
|
|
37
|
+
) @function_node
|
|
38
|
+
|
|
39
|
+
(assignment_expression
|
|
40
|
+
left: (member_expression
|
|
41
|
+
property: (property_identifier) @name
|
|
42
|
+
)
|
|
43
|
+
right: (function_expression
|
|
44
|
+
parameters: (formal_parameters) @params
|
|
45
|
+
) @function_node
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
(assignment_expression
|
|
49
|
+
left: (member_expression
|
|
50
|
+
property: (property_identifier) @name
|
|
51
|
+
)
|
|
52
|
+
right: (arrow_function
|
|
53
|
+
parameters: (formal_parameters) @params
|
|
54
|
+
) @function_node
|
|
55
|
+
)
|
|
56
|
+
""",
|
|
57
|
+
"classes": """
|
|
58
|
+
(class_declaration) @class
|
|
59
|
+
(abstract_class_declaration) @class
|
|
60
|
+
(class) @class
|
|
61
|
+
""",
|
|
62
|
+
"interfaces": """
|
|
63
|
+
(interface_declaration
|
|
64
|
+
name: (type_identifier) @name
|
|
65
|
+
) @interface_node
|
|
66
|
+
""",
|
|
67
|
+
"type_aliases": """
|
|
68
|
+
(type_alias_declaration
|
|
69
|
+
name: (type_identifier) @name
|
|
70
|
+
) @type_alias_node
|
|
71
|
+
""",
|
|
72
|
+
"imports": """
|
|
73
|
+
(import_statement) @import
|
|
74
|
+
(call_expression
|
|
75
|
+
function: (identifier) @require_call (#eq? @require_call "require")
|
|
76
|
+
) @import
|
|
77
|
+
""",
|
|
78
|
+
"calls": """
|
|
79
|
+
(call_expression function: (identifier) @name)
|
|
80
|
+
(call_expression function: (member_expression property: (property_identifier) @name))
|
|
81
|
+
(new_expression constructor: (identifier) @name)
|
|
82
|
+
(new_expression constructor: (member_expression property: (property_identifier) @name))
|
|
83
|
+
""",
|
|
84
|
+
"variables": """
|
|
85
|
+
(variable_declarator name: (identifier) @name)
|
|
86
|
+
""",
|
|
87
|
+
"docstrings": """
|
|
88
|
+
(comment) @docstring_comment
|
|
89
|
+
""",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
def is_typescript_file(path: Path) -> bool:
|
|
93
|
+
return path.suffix in {".ts", ".tsx"}
|
|
94
|
+
|
|
95
|
+
class TypescriptTreeSitterParser:
|
|
96
|
+
"""A TypeScript-specific parser using tree-sitter, encapsulating language-specific logic."""
|
|
97
|
+
|
|
98
|
+
def __init__(self, generic_parser_wrapper):
|
|
99
|
+
self.generic_parser_wrapper = generic_parser_wrapper
|
|
100
|
+
self.language_name = generic_parser_wrapper.language_name
|
|
101
|
+
self.language = generic_parser_wrapper.language
|
|
102
|
+
self.parser = generic_parser_wrapper.parser
|
|
103
|
+
|
|
104
|
+
def _get_node_text(self, node) -> str:
|
|
105
|
+
return node.text.decode('utf-8')
|
|
106
|
+
|
|
107
|
+
def _get_parent_context(self, node, types=('function_declaration', 'class_declaration', 'method_definition', 'function_expression', 'arrow_function')):
|
|
108
|
+
curr = node.parent
|
|
109
|
+
while curr:
|
|
110
|
+
if curr.type in types:
|
|
111
|
+
name_node = curr.child_by_field_name('name')
|
|
112
|
+
if not name_node and curr.type in ('function_expression', 'arrow_function'):
|
|
113
|
+
# Try to find name from variable declaration
|
|
114
|
+
if curr.parent and curr.parent.type == 'variable_declarator':
|
|
115
|
+
name_node = curr.parent.child_by_field_name('name')
|
|
116
|
+
elif curr.parent and curr.parent.type == 'assignment_expression':
|
|
117
|
+
name_node = curr.parent.child_by_field_name('left')
|
|
118
|
+
elif curr.parent and curr.parent.type == 'pair': # property: function
|
|
119
|
+
name_node = curr.parent.child_by_field_name('key')
|
|
120
|
+
|
|
121
|
+
return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
|
|
122
|
+
curr = curr.parent
|
|
123
|
+
return None, None, None
|
|
124
|
+
|
|
125
|
+
def _calculate_complexity(self, node):
|
|
126
|
+
complexity_nodes = {
|
|
127
|
+
"if_statement", "for_statement", "while_statement", "do_statement",
|
|
128
|
+
"switch_statement", "case_statement", "conditional_expression",
|
|
129
|
+
"logical_expression", "binary_expression", "catch_clause"
|
|
130
|
+
}
|
|
131
|
+
count = 1
|
|
132
|
+
def traverse(n):
|
|
133
|
+
nonlocal count
|
|
134
|
+
if n.type in complexity_nodes:
|
|
135
|
+
count += 1
|
|
136
|
+
for child in n.children:
|
|
137
|
+
traverse(child)
|
|
138
|
+
traverse(node)
|
|
139
|
+
return count
|
|
140
|
+
|
|
141
|
+
def _get_docstring(self, body_node):
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False) -> Dict:
|
|
145
|
+
self.index_source = index_source
|
|
146
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
147
|
+
source_code = f.read()
|
|
148
|
+
tree = self.parser.parse(bytes(source_code, "utf8"))
|
|
149
|
+
root_node = tree.root_node
|
|
150
|
+
|
|
151
|
+
functions = self._find_functions(root_node)
|
|
152
|
+
classes = self._find_classes(root_node)
|
|
153
|
+
interfaces = self._find_interfaces(root_node)
|
|
154
|
+
type_aliases = self._find_type_aliases(root_node)
|
|
155
|
+
imports = self._find_imports(root_node)
|
|
156
|
+
function_calls = self._find_calls(root_node)
|
|
157
|
+
variables = self._find_variables(root_node)
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
"path": str(path),
|
|
161
|
+
"functions": functions,
|
|
162
|
+
"classes": classes,
|
|
163
|
+
"interfaces": interfaces,
|
|
164
|
+
"type_aliases": type_aliases,
|
|
165
|
+
"variables": variables,
|
|
166
|
+
"imports": imports,
|
|
167
|
+
"function_calls": function_calls,
|
|
168
|
+
"is_dependency": is_dependency,
|
|
169
|
+
"lang": self.language_name,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _find_functions(self, root_node):
|
|
174
|
+
functions = []
|
|
175
|
+
query_str = TS_QUERIES['functions']
|
|
176
|
+
def _fn_for_name(name_node):
|
|
177
|
+
current = name_node.parent
|
|
178
|
+
while current:
|
|
179
|
+
if current.type in ('function_declaration', 'function', 'arrow_function', 'method_definition'):
|
|
180
|
+
return current
|
|
181
|
+
elif current.type in ('variable_declarator', 'assignment_expression'):
|
|
182
|
+
for child in current.children:
|
|
183
|
+
if child.type in ('function', 'arrow_function'):
|
|
184
|
+
return child
|
|
185
|
+
current = current.parent
|
|
186
|
+
return None
|
|
187
|
+
def _fn_for_params(params_node):
|
|
188
|
+
current = params_node.parent
|
|
189
|
+
while current:
|
|
190
|
+
if current.type in ('function_declaration', 'function', 'arrow_function', 'method_definition'):
|
|
191
|
+
return current
|
|
192
|
+
current = current.parent
|
|
193
|
+
return None
|
|
194
|
+
def _key(n):
|
|
195
|
+
return (n.start_byte, n.end_byte, n.type)
|
|
196
|
+
captures_by_function = {}
|
|
197
|
+
def _bucket_for(node):
|
|
198
|
+
fid = _key(node)
|
|
199
|
+
return captures_by_function.setdefault(fid, {
|
|
200
|
+
'node': node, 'name': None, 'params': None, 'single_param': None
|
|
201
|
+
})
|
|
202
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
203
|
+
if capture_name == 'function_node':
|
|
204
|
+
_bucket_for(node)
|
|
205
|
+
elif capture_name == 'name':
|
|
206
|
+
fn = _fn_for_name(node)
|
|
207
|
+
if fn:
|
|
208
|
+
b = _bucket_for(fn)
|
|
209
|
+
b['name'] = self._get_node_text(node)
|
|
210
|
+
elif capture_name == 'params':
|
|
211
|
+
fn = _fn_for_params(node)
|
|
212
|
+
if fn:
|
|
213
|
+
b = _bucket_for(fn)
|
|
214
|
+
b['params'] = node
|
|
215
|
+
elif capture_name == 'single_param':
|
|
216
|
+
fn = _fn_for_params(node)
|
|
217
|
+
if fn:
|
|
218
|
+
b = _bucket_for(fn)
|
|
219
|
+
b['single_param'] = node
|
|
220
|
+
for _, data in captures_by_function.items():
|
|
221
|
+
func_node = data['node']
|
|
222
|
+
name = data.get('name')
|
|
223
|
+
if not name and func_node.type == 'method_definition':
|
|
224
|
+
nm = func_node.child_by_field_name('name')
|
|
225
|
+
if nm:
|
|
226
|
+
name = self._get_node_text(nm)
|
|
227
|
+
if not name:
|
|
228
|
+
continue
|
|
229
|
+
args = []
|
|
230
|
+
if data.get('params'):
|
|
231
|
+
args = self._extract_parameters(data['params'])
|
|
232
|
+
elif data.get('single_param'):
|
|
233
|
+
args = [self._get_node_text(data['single_param'])]
|
|
234
|
+
context, context_type, _ = self._get_parent_context(func_node)
|
|
235
|
+
class_context = context if context_type == 'class_declaration' else None
|
|
236
|
+
docstring = None
|
|
237
|
+
func_data = {
|
|
238
|
+
"name": name,
|
|
239
|
+
"line_number": func_node.start_point[0] + 1,
|
|
240
|
+
"end_line": func_node.end_point[0] + 1,
|
|
241
|
+
"args": args,
|
|
242
|
+
"cyclomatic_complexity": self._calculate_complexity(func_node),
|
|
243
|
+
"context": context,
|
|
244
|
+
"context_type": context_type,
|
|
245
|
+
"class_context": class_context,
|
|
246
|
+
"decorators": [],
|
|
247
|
+
"lang": self.language_name,
|
|
248
|
+
"is_dependency": False,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if self.index_source:
|
|
252
|
+
func_data["source"] = self._get_node_text(func_node)
|
|
253
|
+
func_data["docstring"] = docstring
|
|
254
|
+
functions.append(func_data)
|
|
255
|
+
return functions
|
|
256
|
+
|
|
257
|
+
def _extract_parameters(self, params_node):
|
|
258
|
+
params = []
|
|
259
|
+
if params_node.type == 'formal_parameters':
|
|
260
|
+
for child in params_node.children:
|
|
261
|
+
if child.type == 'identifier':
|
|
262
|
+
params.append(self._get_node_text(child))
|
|
263
|
+
elif child.type == 'required_parameter':
|
|
264
|
+
# required_parameter -> pattern (identifier) + type_annotation
|
|
265
|
+
pattern = child.child_by_field_name('pattern')
|
|
266
|
+
if pattern:
|
|
267
|
+
params.append(self._get_node_text(pattern))
|
|
268
|
+
else:
|
|
269
|
+
# Fallback: first child that is an identifier or pattern
|
|
270
|
+
for sub in child.children:
|
|
271
|
+
if sub.type in ('identifier', 'object_pattern', 'array_pattern'):
|
|
272
|
+
params.append(self._get_node_text(sub))
|
|
273
|
+
break
|
|
274
|
+
elif child.type == 'optional_parameter':
|
|
275
|
+
pattern = child.child_by_field_name('pattern')
|
|
276
|
+
if pattern:
|
|
277
|
+
params.append(self._get_node_text(pattern))
|
|
278
|
+
elif child.type == 'assignment_pattern':
|
|
279
|
+
left_child = child.child_by_field_name('left')
|
|
280
|
+
if left_child and left_child.type == 'identifier':
|
|
281
|
+
params.append(self._get_node_text(left_child))
|
|
282
|
+
elif child.type == 'rest_pattern':
|
|
283
|
+
argument = child.child_by_field_name('argument')
|
|
284
|
+
if argument and argument.type == 'identifier':
|
|
285
|
+
params.append(f"...{self._get_node_text(argument)}")
|
|
286
|
+
return params
|
|
287
|
+
|
|
288
|
+
def _find_classes(self, root_node):
|
|
289
|
+
classes = []
|
|
290
|
+
query_str = TS_QUERIES['classes']
|
|
291
|
+
for class_node, capture_name in execute_query(self.language, query_str, root_node):
|
|
292
|
+
if capture_name == 'class':
|
|
293
|
+
name_node = class_node.child_by_field_name('name')
|
|
294
|
+
if not name_node: continue
|
|
295
|
+
name = self._get_node_text(name_node)
|
|
296
|
+
bases = []
|
|
297
|
+
heritage_node = next((child for child in class_node.children if child.type == 'class_heritage'), None)
|
|
298
|
+
if heritage_node:
|
|
299
|
+
for child in heritage_node.children:
|
|
300
|
+
if child.type == 'extends_clause':
|
|
301
|
+
# extends_clause -> extends identifier
|
|
302
|
+
for sub in child.children:
|
|
303
|
+
if sub.type in ('identifier', 'type_identifier', 'member_expression'):
|
|
304
|
+
bases.append(self._get_node_text(sub))
|
|
305
|
+
elif child.type == 'implements_clause':
|
|
306
|
+
# implements_clause -> implements identifier, identifier...
|
|
307
|
+
for sub in child.children:
|
|
308
|
+
if sub.type in ('identifier', 'type_identifier', 'member_expression'):
|
|
309
|
+
bases.append(self._get_node_text(sub))
|
|
310
|
+
class_data = {
|
|
311
|
+
"name": name,
|
|
312
|
+
"line_number": class_node.start_point[0] + 1,
|
|
313
|
+
"end_line": class_node.end_point[0] + 1,
|
|
314
|
+
"bases": bases,
|
|
315
|
+
"context": None,
|
|
316
|
+
"decorators": [],
|
|
317
|
+
"lang": self.language_name,
|
|
318
|
+
"is_dependency": False,
|
|
319
|
+
}
|
|
320
|
+
if self.index_source:
|
|
321
|
+
class_data["source"] = self._get_node_text(class_node)
|
|
322
|
+
class_data["docstring"] = self._get_docstring(class_node)
|
|
323
|
+
classes.append(class_data)
|
|
324
|
+
return classes
|
|
325
|
+
|
|
326
|
+
def _find_interfaces(self, root_node):
|
|
327
|
+
interfaces = []
|
|
328
|
+
query_str = TS_QUERIES['interfaces']
|
|
329
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
330
|
+
if capture_name == 'interface_node':
|
|
331
|
+
name_node = node.child_by_field_name('name')
|
|
332
|
+
if not name_node: continue
|
|
333
|
+
|
|
334
|
+
name = self._get_node_text(name_node)
|
|
335
|
+
interface_data = {
|
|
336
|
+
"name": name,
|
|
337
|
+
"line_number": node.start_point[0] + 1,
|
|
338
|
+
"end_line": node.end_point[0] + 1,
|
|
339
|
+
"end_line": node.end_point[0] + 1,
|
|
340
|
+
}
|
|
341
|
+
if self.index_source:
|
|
342
|
+
interface_data["source"] = self._get_node_text(node)
|
|
343
|
+
interfaces.append(interface_data)
|
|
344
|
+
return interfaces
|
|
345
|
+
|
|
346
|
+
def _find_type_aliases(self, root_node):
|
|
347
|
+
type_aliases = []
|
|
348
|
+
query_str = TS_QUERIES['type_aliases']
|
|
349
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
350
|
+
if capture_name == 'type_alias_node':
|
|
351
|
+
name_node = node.child_by_field_name('name')
|
|
352
|
+
if not name_node: continue
|
|
353
|
+
|
|
354
|
+
name = self._get_node_text(name_node)
|
|
355
|
+
type_alias_data = {
|
|
356
|
+
"name": name,
|
|
357
|
+
"line_number": node.start_point[0] + 1,
|
|
358
|
+
"end_line": node.end_point[0] + 1,
|
|
359
|
+
"end_line": node.end_point[0] + 1,
|
|
360
|
+
}
|
|
361
|
+
if self.index_source:
|
|
362
|
+
type_alias_data["source"] = self._get_node_text(node)
|
|
363
|
+
type_aliases.append(type_alias_data)
|
|
364
|
+
return type_aliases
|
|
365
|
+
|
|
366
|
+
def _find_imports(self, root_node):
|
|
367
|
+
imports = []
|
|
368
|
+
query_str = TS_QUERIES['imports']
|
|
369
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
370
|
+
if capture_name != 'import':
|
|
371
|
+
continue
|
|
372
|
+
line_number = node.start_point[0] + 1
|
|
373
|
+
if node.type == 'import_statement':
|
|
374
|
+
source = self._get_node_text(node.child_by_field_name('source')).strip('\'"')
|
|
375
|
+
import_clause = node.child_by_field_name('import')
|
|
376
|
+
if not import_clause:
|
|
377
|
+
imports.append({'name': source, 'source': source, 'alias': None, 'line_number': line_number,
|
|
378
|
+
'lang': self.language_name})
|
|
379
|
+
continue
|
|
380
|
+
if import_clause.type == 'identifier':
|
|
381
|
+
alias = self._get_node_text(import_clause)
|
|
382
|
+
imports.append({'name': 'default', 'source': source, 'alias': alias, 'line_number': line_number,
|
|
383
|
+
'lang': self.language_name})
|
|
384
|
+
elif import_clause.type == 'namespace_import':
|
|
385
|
+
alias_node = import_clause.child_by_field_name('alias')
|
|
386
|
+
if alias_node:
|
|
387
|
+
alias = self._get_node_text(alias_node)
|
|
388
|
+
imports.append({'name': '*', 'source': source, 'alias': alias, 'line_number': line_number,
|
|
389
|
+
'lang': self.language_name})
|
|
390
|
+
elif import_clause.type == 'named_imports':
|
|
391
|
+
for specifier in import_clause.children:
|
|
392
|
+
if specifier.type == 'import_specifier':
|
|
393
|
+
name_node = specifier.child_by_field_name('name')
|
|
394
|
+
alias_node = specifier.child_by_field_name('alias')
|
|
395
|
+
original_name = self._get_node_text(name_node)
|
|
396
|
+
alias = self._get_node_text(alias_node) if alias_node else None
|
|
397
|
+
imports.append(
|
|
398
|
+
{'name': original_name, 'source': source, 'alias': alias, 'line_number': line_number,
|
|
399
|
+
'lang': self.language_name})
|
|
400
|
+
elif node.type == 'call_expression':
|
|
401
|
+
args = node.child_by_field_name('arguments')
|
|
402
|
+
if not args or args.named_child_count == 0: continue
|
|
403
|
+
source_node = args.named_child(0)
|
|
404
|
+
if not source_node or source_node.type != 'string': continue
|
|
405
|
+
source = self._get_node_text(source_node).strip('\'"')
|
|
406
|
+
alias = None
|
|
407
|
+
if node.parent.type == 'variable_declarator':
|
|
408
|
+
alias_node = node.parent.child_by_field_name('name')
|
|
409
|
+
if alias_node:
|
|
410
|
+
alias = self._get_node_text(alias_node)
|
|
411
|
+
imports.append({'name': source, 'source': source, 'alias': alias, 'line_number': line_number,
|
|
412
|
+
'lang': self.language_name})
|
|
413
|
+
return imports
|
|
414
|
+
|
|
415
|
+
def _find_calls(self, root_node):
|
|
416
|
+
calls = []
|
|
417
|
+
query_str = TS_QUERIES['calls']
|
|
418
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
419
|
+
if capture_name == 'name':
|
|
420
|
+
# Traverse up to find the call/new expression
|
|
421
|
+
call_node = node.parent
|
|
422
|
+
while call_node and call_node.type not in ('call_expression', 'new_expression') and call_node.type != 'program':
|
|
423
|
+
call_node = call_node.parent
|
|
424
|
+
|
|
425
|
+
name = self._get_node_text(node)
|
|
426
|
+
|
|
427
|
+
# Improved args extraction
|
|
428
|
+
args = []
|
|
429
|
+
arguments_node = None
|
|
430
|
+
if call_node and call_node.type in ('call_expression', 'new_expression'):
|
|
431
|
+
arguments_node = call_node.child_by_field_name('arguments')
|
|
432
|
+
|
|
433
|
+
if arguments_node:
|
|
434
|
+
for arg in arguments_node.children:
|
|
435
|
+
if arg.type not in ('(', ')', ','):
|
|
436
|
+
args.append(self._get_node_text(arg))
|
|
437
|
+
|
|
438
|
+
call_data = {
|
|
439
|
+
"name": name,
|
|
440
|
+
"full_name": self._get_node_text(call_node) if call_node else name,
|
|
441
|
+
"line_number": node.start_point[0] + 1,
|
|
442
|
+
"args": args,
|
|
443
|
+
"inferred_obj_type": None,
|
|
444
|
+
"context": self._get_parent_context(node),
|
|
445
|
+
"class_context": self._get_parent_context(node, types=('class_declaration', 'abstract_class_declaration')),
|
|
446
|
+
"lang": self.language_name,
|
|
447
|
+
"is_dependency": False,
|
|
448
|
+
}
|
|
449
|
+
calls.append(call_data)
|
|
450
|
+
return calls
|
|
451
|
+
|
|
452
|
+
def _find_variables(self, root_node):
|
|
453
|
+
variables = []
|
|
454
|
+
query_str = TS_QUERIES['variables']
|
|
455
|
+
for match in execute_query(self.language, query_str, root_node):
|
|
456
|
+
capture_name = match[1]
|
|
457
|
+
node = match[0]
|
|
458
|
+
if capture_name == 'name':
|
|
459
|
+
var_node = node.parent
|
|
460
|
+
name = self._get_node_text(node)
|
|
461
|
+
value = None
|
|
462
|
+
type_text = None
|
|
463
|
+
|
|
464
|
+
# Detect if variable assigned to a function
|
|
465
|
+
value_node = var_node.child_by_field_name("value") if var_node else None
|
|
466
|
+
|
|
467
|
+
if value_node:
|
|
468
|
+
value_type = value_node.type
|
|
469
|
+
|
|
470
|
+
# --- Skip variables that are assigned a function ---
|
|
471
|
+
if value_type in ("function_expression", "arrow_function"):
|
|
472
|
+
continue
|
|
473
|
+
|
|
474
|
+
if "function" in value_type or "arrow" in value_type:
|
|
475
|
+
continue
|
|
476
|
+
|
|
477
|
+
# --- Handle various assignment types ---
|
|
478
|
+
if value_type == "call_expression":
|
|
479
|
+
func_node = value_node.child_by_field_name("function")
|
|
480
|
+
value = self._get_node_text(func_node) if func_node else name
|
|
481
|
+
else:
|
|
482
|
+
value = self._get_node_text(value_node)
|
|
483
|
+
|
|
484
|
+
context, context_type, context_line = self._get_parent_context(node)
|
|
485
|
+
class_context = context if context_type == 'class_declaration' else None
|
|
486
|
+
|
|
487
|
+
variable_data = {
|
|
488
|
+
"name": name,
|
|
489
|
+
"line_number": node.start_point[0] + 1,
|
|
490
|
+
"value": value,
|
|
491
|
+
"type": type_text,
|
|
492
|
+
"context": context,
|
|
493
|
+
"class_context": class_context,
|
|
494
|
+
"lang": self.language_name,
|
|
495
|
+
"is_dependency": False,
|
|
496
|
+
}
|
|
497
|
+
variables.append(variable_data)
|
|
498
|
+
return variables
|
|
499
|
+
|
|
500
|
+
def pre_scan_typescript(files: list[Path], parser_wrapper) -> dict:
|
|
501
|
+
"""Scans TypeScript files to create a map of class/function names to their file paths."""
|
|
502
|
+
imports_map = {}
|
|
503
|
+
|
|
504
|
+
# Simplified queries that capture the parent nodes, then extract names manually
|
|
505
|
+
query_strings = [
|
|
506
|
+
"(class_declaration) @class",
|
|
507
|
+
"(function_declaration) @function",
|
|
508
|
+
"(variable_declarator) @var_decl",
|
|
509
|
+
"(method_definition) @method",
|
|
510
|
+
"(interface_declaration) @interface",
|
|
511
|
+
"(type_alias_declaration) @type_alias",
|
|
512
|
+
]
|
|
513
|
+
|
|
514
|
+
for path in files:
|
|
515
|
+
try:
|
|
516
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
517
|
+
source_code = f.read()
|
|
518
|
+
tree = parser_wrapper.parser.parse(bytes(source_code, "utf8"))
|
|
519
|
+
|
|
520
|
+
# Run each query separately
|
|
521
|
+
for query_str in query_strings:
|
|
522
|
+
try:
|
|
523
|
+
|
|
524
|
+
for node, capture_name in execute_query(parser_wrapper.language, query_str, tree.root_node):
|
|
525
|
+
name = None
|
|
526
|
+
|
|
527
|
+
# Extract name based on node type
|
|
528
|
+
if capture_name == 'class':
|
|
529
|
+
name_node = node.child_by_field_name('name')
|
|
530
|
+
if name_node:
|
|
531
|
+
name = name_node.text.decode('utf-8')
|
|
532
|
+
|
|
533
|
+
elif capture_name == 'function':
|
|
534
|
+
name_node = node.child_by_field_name('name')
|
|
535
|
+
if name_node:
|
|
536
|
+
name = name_node.text.decode('utf-8')
|
|
537
|
+
|
|
538
|
+
elif capture_name == 'var_decl':
|
|
539
|
+
# Check if it's a function or arrow function
|
|
540
|
+
name_node = node.child_by_field_name('name')
|
|
541
|
+
value_node = node.child_by_field_name('value')
|
|
542
|
+
if name_node and value_node:
|
|
543
|
+
if value_node.type in ('function', 'arrow_function'):
|
|
544
|
+
name = name_node.text.decode('utf-8')
|
|
545
|
+
|
|
546
|
+
elif capture_name == 'method':
|
|
547
|
+
name_node = node.child_by_field_name('name')
|
|
548
|
+
if name_node:
|
|
549
|
+
name = name_node.text.decode('utf-8')
|
|
550
|
+
|
|
551
|
+
elif capture_name == 'interface':
|
|
552
|
+
name_node = node.child_by_field_name('name')
|
|
553
|
+
if name_node:
|
|
554
|
+
name = name_node.text.decode('utf-8')
|
|
555
|
+
|
|
556
|
+
elif capture_name == 'type_alias':
|
|
557
|
+
name_node = node.child_by_field_name('name')
|
|
558
|
+
if name_node:
|
|
559
|
+
name = name_node.text.decode('utf-8')
|
|
560
|
+
|
|
561
|
+
# Add to imports map if we found a name
|
|
562
|
+
if name:
|
|
563
|
+
if name not in imports_map:
|
|
564
|
+
imports_map[name] = []
|
|
565
|
+
file_path_str = str(path.resolve())
|
|
566
|
+
if file_path_str not in imports_map[name]:
|
|
567
|
+
imports_map[name].append(file_path_str)
|
|
568
|
+
|
|
569
|
+
except Exception as query_error:
|
|
570
|
+
warning_logger(f"Query failed for pattern '{query_str}': {query_error}")
|
|
571
|
+
|
|
572
|
+
except Exception as e:
|
|
573
|
+
warning_logger(f"Tree-sitter pre-scan failed for {path}: {e}")
|
|
574
|
+
|
|
575
|
+
return imports_map
|