codegraphcontext-rust 0.5.5__cp310-cp310-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.cp310-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,589 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, Optional, Tuple
|
|
3
|
+
import re
|
|
4
|
+
from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger
|
|
5
|
+
from codegraphcontext.utils.tree_sitter_manager import execute_query
|
|
6
|
+
|
|
7
|
+
# --- Helpers to classify JS methods ---
|
|
8
|
+
_GETTER_RE = re.compile(r"^\s*(?:static\s+)?get\b")
|
|
9
|
+
_SETTER_RE = re.compile(r"^\s*(?:static\s+)?set\b")
|
|
10
|
+
_STATIC_RE = re.compile(r"^\s*static\b")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _first_line_before_body(text: str) -> str:
|
|
14
|
+
"""
|
|
15
|
+
Best-effort header extraction: take text before the first '{'
|
|
16
|
+
(covers class/object methods). Fallback to the first line.
|
|
17
|
+
"""
|
|
18
|
+
head = text.split("{", 1)[0]
|
|
19
|
+
if not head.strip():
|
|
20
|
+
return text.splitlines()[0] if text.splitlines() else text
|
|
21
|
+
return head
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _classify_method_kind(header: str) -> Optional[str]:
|
|
25
|
+
"""
|
|
26
|
+
Return 'getter' | 'setter' | 'static' | None.
|
|
27
|
+
Prefer 'getter'/'setter' over 'static' when both appear.
|
|
28
|
+
"""
|
|
29
|
+
if _GETTER_RE.search(header):
|
|
30
|
+
return "getter"
|
|
31
|
+
if _SETTER_RE.search(header):
|
|
32
|
+
return "setter"
|
|
33
|
+
if _STATIC_RE.search(header):
|
|
34
|
+
return "static"
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
JS_QUERIES = {
|
|
39
|
+
"functions": """
|
|
40
|
+
(function_declaration
|
|
41
|
+
name: (identifier) @name
|
|
42
|
+
parameters: (formal_parameters) @params
|
|
43
|
+
) @function_node
|
|
44
|
+
|
|
45
|
+
(variable_declarator
|
|
46
|
+
name: (identifier) @name
|
|
47
|
+
value: (function_expression
|
|
48
|
+
parameters: (formal_parameters) @params
|
|
49
|
+
) @function_node
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
(variable_declarator
|
|
53
|
+
name: (identifier) @name
|
|
54
|
+
value: (arrow_function
|
|
55
|
+
parameters: (formal_parameters) @params
|
|
56
|
+
) @function_node
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
(variable_declarator
|
|
60
|
+
name: (identifier) @name
|
|
61
|
+
value: (arrow_function
|
|
62
|
+
parameter: (identifier) @single_param
|
|
63
|
+
) @function_node
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
(method_definition
|
|
67
|
+
name: (property_identifier) @name
|
|
68
|
+
parameters: (formal_parameters) @params
|
|
69
|
+
) @function_node
|
|
70
|
+
|
|
71
|
+
(assignment_expression
|
|
72
|
+
left: (member_expression
|
|
73
|
+
property: (property_identifier) @name
|
|
74
|
+
)
|
|
75
|
+
right: (function_expression
|
|
76
|
+
parameters: (formal_parameters) @params
|
|
77
|
+
) @function_node
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
(assignment_expression
|
|
81
|
+
left: (member_expression
|
|
82
|
+
property: (property_identifier) @name
|
|
83
|
+
)
|
|
84
|
+
right: (arrow_function
|
|
85
|
+
parameters: (formal_parameters) @params
|
|
86
|
+
) @function_node
|
|
87
|
+
)
|
|
88
|
+
""",
|
|
89
|
+
"classes": """
|
|
90
|
+
(class_declaration) @class
|
|
91
|
+
(class) @class
|
|
92
|
+
""",
|
|
93
|
+
"imports": """
|
|
94
|
+
(import_statement) @import
|
|
95
|
+
(call_expression
|
|
96
|
+
function: (identifier) @require_call (#eq? @require_call "require")
|
|
97
|
+
) @import
|
|
98
|
+
""",
|
|
99
|
+
"calls": """
|
|
100
|
+
(call_expression function: (identifier) @name)
|
|
101
|
+
(call_expression function: (member_expression property: (property_identifier) @name))
|
|
102
|
+
(new_expression constructor: (identifier) @name)
|
|
103
|
+
(new_expression constructor: (member_expression property: (property_identifier) @name))
|
|
104
|
+
""",
|
|
105
|
+
"variables": """
|
|
106
|
+
(variable_declarator name: (identifier) @name)
|
|
107
|
+
""",
|
|
108
|
+
"docstrings": """
|
|
109
|
+
(comment) @docstring_comment
|
|
110
|
+
""",
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class JavascriptTreeSitterParser:
|
|
115
|
+
"""A JavaScript-specific parser using tree-sitter, encapsulating language-specific logic."""
|
|
116
|
+
|
|
117
|
+
def __init__(self, generic_parser_wrapper):
|
|
118
|
+
self.generic_parser_wrapper = generic_parser_wrapper
|
|
119
|
+
self.language_name = generic_parser_wrapper.language_name
|
|
120
|
+
self.language = generic_parser_wrapper.language
|
|
121
|
+
self.parser = generic_parser_wrapper.parser
|
|
122
|
+
|
|
123
|
+
def _get_node_text(self, node) -> str:
|
|
124
|
+
return node.text.decode('utf-8')
|
|
125
|
+
|
|
126
|
+
def _get_parent_context(self, node, types=('function_declaration', 'class_declaration', 'function_expression', 'method_definition', 'arrow_function')):
|
|
127
|
+
# JS specific context types
|
|
128
|
+
curr = node.parent
|
|
129
|
+
while curr:
|
|
130
|
+
if curr.type in types:
|
|
131
|
+
name_node = curr.child_by_field_name('name')
|
|
132
|
+
if not name_node and curr.type in ('function_expression', 'arrow_function'):
|
|
133
|
+
# Try to find name from variable declaration
|
|
134
|
+
if curr.parent and curr.parent.type == 'variable_declarator':
|
|
135
|
+
name_node = curr.parent.child_by_field_name('name')
|
|
136
|
+
elif curr.parent and curr.parent.type == 'assignment_expression':
|
|
137
|
+
name_node = curr.parent.child_by_field_name('left')
|
|
138
|
+
elif curr.parent and curr.parent.type == 'pair': # property: function
|
|
139
|
+
name_node = curr.parent.child_by_field_name('key')
|
|
140
|
+
|
|
141
|
+
return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
|
|
142
|
+
curr = curr.parent
|
|
143
|
+
return None, None, None
|
|
144
|
+
|
|
145
|
+
def _calculate_complexity(self, node):
|
|
146
|
+
# JS specific complexity nodes
|
|
147
|
+
complexity_nodes = {
|
|
148
|
+
"if_statement", "for_statement", "while_statement", "do_statement",
|
|
149
|
+
"switch_statement", "case_statement", "conditional_expression",
|
|
150
|
+
"logical_expression", "binary_expression", "catch_clause"
|
|
151
|
+
}
|
|
152
|
+
count = 1
|
|
153
|
+
|
|
154
|
+
def traverse(n):
|
|
155
|
+
nonlocal count
|
|
156
|
+
if n.type in complexity_nodes:
|
|
157
|
+
count += 1
|
|
158
|
+
for child in n.children:
|
|
159
|
+
traverse(child)
|
|
160
|
+
|
|
161
|
+
traverse(node)
|
|
162
|
+
return count
|
|
163
|
+
|
|
164
|
+
def _get_docstring(self, body_node):
|
|
165
|
+
# JS specific docstring extraction (e.g., JSDoc comments)
|
|
166
|
+
# This is a placeholder and needs more sophisticated logic
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False) -> Dict[str, Any]:
|
|
170
|
+
"""Parses a file and returns its structure in a standardized dictionary format."""
|
|
171
|
+
self.index_source = index_source
|
|
172
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
173
|
+
source_code = f.read()
|
|
174
|
+
|
|
175
|
+
tree = self.parser.parse(bytes(source_code, "utf8"))
|
|
176
|
+
root_node = tree.root_node
|
|
177
|
+
|
|
178
|
+
functions = self._find_functions(root_node)
|
|
179
|
+
classes = self._find_classes(root_node)
|
|
180
|
+
imports = self._find_imports(root_node)
|
|
181
|
+
function_calls = self._find_calls(root_node)
|
|
182
|
+
variables = self._find_variables(root_node)
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
"path": str(path),
|
|
186
|
+
"functions": functions,
|
|
187
|
+
"classes": classes,
|
|
188
|
+
"variables": variables,
|
|
189
|
+
"imports": imports,
|
|
190
|
+
"function_calls": function_calls,
|
|
191
|
+
"is_dependency": is_dependency,
|
|
192
|
+
"lang": self.language_name,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
def _find_functions(self, root_node):
|
|
196
|
+
functions = []
|
|
197
|
+
query_str = JS_QUERIES['functions']
|
|
198
|
+
|
|
199
|
+
# Local helpers so we don't depend on class attrs being present
|
|
200
|
+
def _fn_for_name(name_node):
|
|
201
|
+
current = name_node.parent
|
|
202
|
+
while current:
|
|
203
|
+
if current.type in ('function_declaration', 'function', 'arrow_function', 'method_definition', 'function_expression'):
|
|
204
|
+
return current
|
|
205
|
+
elif current.type in ('variable_declarator', 'assignment_expression'):
|
|
206
|
+
for child in current.children:
|
|
207
|
+
if child.type in ('function', 'arrow_function', 'function_expression'):
|
|
208
|
+
return child
|
|
209
|
+
current = current.parent
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
def _fn_for_params(params_node):
|
|
213
|
+
current = params_node.parent
|
|
214
|
+
while current:
|
|
215
|
+
if current.type in ('function_declaration', 'function', 'arrow_function', 'method_definition', 'function_expression'):
|
|
216
|
+
return current
|
|
217
|
+
current = current.parent
|
|
218
|
+
return None
|
|
219
|
+
# stable keys so the same function only gets one bucket
|
|
220
|
+
def _key(n): # start/end byte + type is stable across captures
|
|
221
|
+
return (n.start_byte, n.end_byte, n.type)
|
|
222
|
+
|
|
223
|
+
# Collect captures grouped by function node
|
|
224
|
+
captures_by_function = {}
|
|
225
|
+
def _bucket_for(node):
|
|
226
|
+
fid = _key(node)
|
|
227
|
+
return captures_by_function.setdefault(fid, {
|
|
228
|
+
'node': node, 'name': None, 'params': None, 'single_param': None
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
232
|
+
if capture_name == 'function_node':
|
|
233
|
+
_bucket_for(node)
|
|
234
|
+
elif capture_name == 'name':
|
|
235
|
+
fn = _fn_for_name(node)
|
|
236
|
+
if fn:
|
|
237
|
+
b = _bucket_for(fn)
|
|
238
|
+
b['name'] = self._get_node_text(node)
|
|
239
|
+
elif capture_name == 'params':
|
|
240
|
+
fn = _fn_for_params(node)
|
|
241
|
+
if fn:
|
|
242
|
+
b = _bucket_for(fn)
|
|
243
|
+
b['params'] = node
|
|
244
|
+
elif capture_name == 'single_param':
|
|
245
|
+
fn = _fn_for_params(node)
|
|
246
|
+
if fn:
|
|
247
|
+
b = _bucket_for(fn)
|
|
248
|
+
b['single_param'] = node
|
|
249
|
+
|
|
250
|
+
# Build Function entries
|
|
251
|
+
for _, data in captures_by_function.items():
|
|
252
|
+
func_node = data['node']
|
|
253
|
+
|
|
254
|
+
# Backfill name for method_definition if query didn't capture it
|
|
255
|
+
name = data.get('name')
|
|
256
|
+
if not name and func_node.type == 'method_definition':
|
|
257
|
+
nm = func_node.child_by_field_name('name')
|
|
258
|
+
if nm:
|
|
259
|
+
name = self._get_node_text(nm)
|
|
260
|
+
if not name:
|
|
261
|
+
continue # skip nameless functions
|
|
262
|
+
|
|
263
|
+
# Parameters
|
|
264
|
+
args = []
|
|
265
|
+
if data.get('params'):
|
|
266
|
+
args = self._extract_parameters(data['params'])
|
|
267
|
+
elif data.get('single_param'):
|
|
268
|
+
args = [self._get_node_text(data['single_param'])]
|
|
269
|
+
|
|
270
|
+
# Context & docstring
|
|
271
|
+
context, context_type, _ = self._get_parent_context(func_node)
|
|
272
|
+
class_context = context if context_type == 'class_declaration' else None
|
|
273
|
+
docstring = self._get_jsdoc_comment(func_node)
|
|
274
|
+
|
|
275
|
+
# Classify getter/setter/static (methods only)
|
|
276
|
+
js_kind = None
|
|
277
|
+
if func_node.type == 'method_definition':
|
|
278
|
+
header = _first_line_before_body(self._get_node_text(func_node))
|
|
279
|
+
js_kind = _classify_method_kind(header)
|
|
280
|
+
|
|
281
|
+
func_data = {
|
|
282
|
+
"name": name,
|
|
283
|
+
"line_number": func_node.start_point[0] + 1,
|
|
284
|
+
"end_line": func_node.end_point[0] + 1,
|
|
285
|
+
"args": args,
|
|
286
|
+
"lang": self.language_name,
|
|
287
|
+
"is_dependency": False,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if self.index_source:
|
|
291
|
+
func_data["source"] = self._get_node_text(func_node)
|
|
292
|
+
func_data["docstring"] = docstring
|
|
293
|
+
if js_kind is not None:
|
|
294
|
+
func_data["type"] = js_kind
|
|
295
|
+
|
|
296
|
+
functions.append(func_data)
|
|
297
|
+
|
|
298
|
+
return functions
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _find_function_node_for_name(self, name_node):
|
|
303
|
+
"""Find the function node that contains this name node."""
|
|
304
|
+
current = name_node.parent
|
|
305
|
+
while current:
|
|
306
|
+
if current.type in ('function_declaration', 'function', 'arrow_function', 'method_definition'):
|
|
307
|
+
return current
|
|
308
|
+
elif current.type in ('variable_declarator', 'assignment_expression'):
|
|
309
|
+
# Check if this declarator/assignment contains a function
|
|
310
|
+
for child in current.children:
|
|
311
|
+
if child.type in ('function', 'arrow_function'):
|
|
312
|
+
return child
|
|
313
|
+
current = current.parent
|
|
314
|
+
return None
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _find_function_node_for_params(self, params_node):
|
|
318
|
+
"""Find the function node that contains this parameters node."""
|
|
319
|
+
current = params_node.parent
|
|
320
|
+
while current:
|
|
321
|
+
if current.type in ('function_declaration', 'function', 'arrow_function', 'method_definition'):
|
|
322
|
+
return current
|
|
323
|
+
current = current.parent
|
|
324
|
+
|
|
325
|
+
return None
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _extract_parameters(self, params_node):
|
|
329
|
+
"""Extract parameter names from formal_parameters node."""
|
|
330
|
+
params = []
|
|
331
|
+
if params_node.type == 'formal_parameters':
|
|
332
|
+
for child in params_node.children:
|
|
333
|
+
if child.type == 'identifier':
|
|
334
|
+
params.append(self._get_node_text(child))
|
|
335
|
+
elif child.type == 'assignment_pattern':
|
|
336
|
+
# Default parameter: param = defaultValue
|
|
337
|
+
left_child = child.child_by_field_name('left')
|
|
338
|
+
if left_child and left_child.type == 'identifier':
|
|
339
|
+
params.append(self._get_node_text(left_child))
|
|
340
|
+
elif child.type == 'rest_pattern':
|
|
341
|
+
# Rest parameter: ...args
|
|
342
|
+
argument = child.child_by_field_name('argument')
|
|
343
|
+
if argument and argument.type == 'identifier':
|
|
344
|
+
params.append(f"...{self._get_node_text(argument)}")
|
|
345
|
+
return params
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _get_jsdoc_comment(self, func_node):
|
|
349
|
+
"""Extract JSDoc comment preceding the function."""
|
|
350
|
+
# Look for comments before the function
|
|
351
|
+
prev_sibling = func_node.prev_sibling
|
|
352
|
+
while prev_sibling and prev_sibling.type in ('comment', '\n', ' '):
|
|
353
|
+
if prev_sibling.type == 'comment':
|
|
354
|
+
comment_text = self._get_node_text(prev_sibling)
|
|
355
|
+
if comment_text.startswith('/**') and comment_text.endswith('*/'):
|
|
356
|
+
return comment_text.strip()
|
|
357
|
+
prev_sibling = prev_sibling.prev_sibling
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _find_classes(self, root_node):
|
|
362
|
+
classes = []
|
|
363
|
+
query_str = JS_QUERIES['classes']
|
|
364
|
+
for class_node, capture_name in execute_query(self.language, query_str, root_node):
|
|
365
|
+
if capture_name == 'class':
|
|
366
|
+
name_node = class_node.child_by_field_name('name')
|
|
367
|
+
if not name_node: continue
|
|
368
|
+
name = self._get_node_text(name_node)
|
|
369
|
+
|
|
370
|
+
bases = []
|
|
371
|
+
heritage_node = next((child for child in class_node.children if child.type == 'class_heritage'), None)
|
|
372
|
+
if heritage_node:
|
|
373
|
+
if heritage_node.named_child_count > 0:
|
|
374
|
+
base_expr_node = heritage_node.named_child(0)
|
|
375
|
+
bases.append(self._get_node_text(base_expr_node))
|
|
376
|
+
elif heritage_node.child_count > 0:
|
|
377
|
+
# Fallback for anonymous nodes
|
|
378
|
+
base_expr_node = heritage_node.child(heritage_node.child_count - 1)
|
|
379
|
+
bases.append(self._get_node_text(base_expr_node))
|
|
380
|
+
|
|
381
|
+
class_data = {
|
|
382
|
+
"name": name,
|
|
383
|
+
"line_number": class_node.start_point[0] + 1,
|
|
384
|
+
"end_line": class_node.end_point[0] + 1,
|
|
385
|
+
"bases": bases,
|
|
386
|
+
"context": None,
|
|
387
|
+
"decorators": [],
|
|
388
|
+
"lang": self.language_name,
|
|
389
|
+
"is_dependency": False,
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if self.index_source:
|
|
393
|
+
class_data["source"] = self._get_node_text(class_node)
|
|
394
|
+
class_data["docstring"] = self._get_docstring(class_node)
|
|
395
|
+
|
|
396
|
+
classes.append(class_data)
|
|
397
|
+
return classes
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _find_imports(self, root_node):
|
|
401
|
+
imports = []
|
|
402
|
+
query_str = JS_QUERIES['imports']
|
|
403
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
404
|
+
if capture_name != 'import':
|
|
405
|
+
continue
|
|
406
|
+
|
|
407
|
+
line_number = node.start_point[0] + 1
|
|
408
|
+
|
|
409
|
+
if node.type == 'import_statement':
|
|
410
|
+
source = self._get_node_text(node.child_by_field_name('source')).strip('\'"')
|
|
411
|
+
|
|
412
|
+
# Look for different import structures
|
|
413
|
+
import_clause = node.child_by_field_name('import')
|
|
414
|
+
if not import_clause:
|
|
415
|
+
imports.append({'name': source, 'source': source, 'alias': None, 'line_number': line_number,
|
|
416
|
+
'lang': self.language_name})
|
|
417
|
+
continue
|
|
418
|
+
|
|
419
|
+
# Default import: import defaultExport from '...'
|
|
420
|
+
if import_clause.type == 'identifier':
|
|
421
|
+
alias = self._get_node_text(import_clause)
|
|
422
|
+
imports.append({'name': 'default', 'source': source, 'alias': alias, 'line_number': line_number,
|
|
423
|
+
'lang': self.language_name})
|
|
424
|
+
|
|
425
|
+
# Namespace import: import * as name from '...'
|
|
426
|
+
elif import_clause.type == 'namespace_import':
|
|
427
|
+
alias_node = import_clause.child_by_field_name('alias')
|
|
428
|
+
if alias_node:
|
|
429
|
+
alias = self._get_node_text(alias_node)
|
|
430
|
+
imports.append({'name': '*', 'source': source, 'alias': alias, 'line_number': line_number,
|
|
431
|
+
'lang': self.language_name})
|
|
432
|
+
|
|
433
|
+
# Named imports: import { name, name as alias } from '...'
|
|
434
|
+
elif import_clause.type == 'named_imports':
|
|
435
|
+
for specifier in import_clause.children:
|
|
436
|
+
if specifier.type == 'import_specifier':
|
|
437
|
+
name_node = specifier.child_by_field_name('name')
|
|
438
|
+
alias_node = specifier.child_by_field_name('alias')
|
|
439
|
+
original_name = self._get_node_text(name_node)
|
|
440
|
+
alias = self._get_node_text(alias_node) if alias_node else None
|
|
441
|
+
imports.append(
|
|
442
|
+
{'name': original_name, 'source': source, 'alias': alias, 'line_number': line_number,
|
|
443
|
+
'lang': self.language_name})
|
|
444
|
+
|
|
445
|
+
elif node.type == 'call_expression': # require('...')
|
|
446
|
+
args = node.child_by_field_name('arguments')
|
|
447
|
+
if not args or args.named_child_count == 0: continue
|
|
448
|
+
source_node = args.named_child(0)
|
|
449
|
+
if not source_node or source_node.type != 'string': continue
|
|
450
|
+
source = self._get_node_text(source_node).strip('\'"')
|
|
451
|
+
|
|
452
|
+
alias = None
|
|
453
|
+
if node.parent.type == 'variable_declarator':
|
|
454
|
+
alias_node = node.parent.child_by_field_name('name')
|
|
455
|
+
if alias_node:
|
|
456
|
+
alias = self._get_node_text(alias_node)
|
|
457
|
+
imports.append({'name': source, 'source': source, 'alias': alias, 'line_number': line_number,
|
|
458
|
+
'lang': self.language_name})
|
|
459
|
+
|
|
460
|
+
return imports
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def _find_calls(self, root_node):
|
|
464
|
+
calls = []
|
|
465
|
+
query_str = JS_QUERIES['calls']
|
|
466
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
467
|
+
# Placeholder for JS call extraction logic
|
|
468
|
+
if capture_name == 'name':
|
|
469
|
+
# Traverse up to find the call_expression
|
|
470
|
+
call_node = node.parent
|
|
471
|
+
while call_node and call_node.type != 'call_expression' and call_node.type != 'program':
|
|
472
|
+
call_node = call_node.parent
|
|
473
|
+
|
|
474
|
+
name = self._get_node_text(node)
|
|
475
|
+
|
|
476
|
+
# Improved args extraction
|
|
477
|
+
args = []
|
|
478
|
+
arguments_node = None
|
|
479
|
+
if call_node and call_node.type in ('call_expression', 'new_expression'):
|
|
480
|
+
arguments_node = call_node.child_by_field_name('arguments')
|
|
481
|
+
|
|
482
|
+
if arguments_node:
|
|
483
|
+
for arg in arguments_node.children:
|
|
484
|
+
if arg.type not in ('(', ')', ','):
|
|
485
|
+
args.append(self._get_node_text(arg))
|
|
486
|
+
|
|
487
|
+
call_data = {
|
|
488
|
+
"name": name,
|
|
489
|
+
"full_name": self._get_node_text(call_node),
|
|
490
|
+
"line_number": node.start_point[0] + 1,
|
|
491
|
+
"args": args,
|
|
492
|
+
"inferred_obj_type": None,
|
|
493
|
+
"context": self._get_parent_context(node),
|
|
494
|
+
"class_context": self._get_parent_context(node, types=('class_declaration',))[:2],
|
|
495
|
+
"lang": self.language_name,
|
|
496
|
+
"is_dependency": False,
|
|
497
|
+
}
|
|
498
|
+
calls.append(call_data)
|
|
499
|
+
return calls
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _find_variables(self, root_node):
|
|
503
|
+
variables = []
|
|
504
|
+
query_str = JS_QUERIES['variables']
|
|
505
|
+
for match in execute_query(self.language, query_str, root_node):
|
|
506
|
+
capture_name = match[1]
|
|
507
|
+
node = match[0]
|
|
508
|
+
|
|
509
|
+
if capture_name == 'name':
|
|
510
|
+
var_node = node.parent
|
|
511
|
+
name = self._get_node_text(node)
|
|
512
|
+
value = None
|
|
513
|
+
type_text = None
|
|
514
|
+
|
|
515
|
+
# Detect if variable assigned to a function
|
|
516
|
+
value_node = var_node.child_by_field_name("value") if var_node else None
|
|
517
|
+
|
|
518
|
+
if value_node:
|
|
519
|
+
value_type = value_node.type
|
|
520
|
+
|
|
521
|
+
# --- Skip variables that are assigned a function ---
|
|
522
|
+
if value_type in ("function_expression", "arrow_function"):
|
|
523
|
+
continue
|
|
524
|
+
|
|
525
|
+
# Some grammars might have async_arrow_function or similar
|
|
526
|
+
if "function" in value_type or "arrow" in value_type:
|
|
527
|
+
continue
|
|
528
|
+
|
|
529
|
+
# --- Handle various assignment types ---
|
|
530
|
+
if value_type == "call_expression":
|
|
531
|
+
func_node = value_node.child_by_field_name("function")
|
|
532
|
+
value = self._get_node_text(func_node) if func_node else name
|
|
533
|
+
else:
|
|
534
|
+
value = self._get_node_text(value_node)
|
|
535
|
+
|
|
536
|
+
context, context_type, context_line = self._get_parent_context(node)
|
|
537
|
+
class_context = context if context_type == 'class_declaration' else None
|
|
538
|
+
|
|
539
|
+
variable_data = {
|
|
540
|
+
"name": name,
|
|
541
|
+
"line_number": node.start_point[0] + 1,
|
|
542
|
+
"value": value,
|
|
543
|
+
"type": type_text,
|
|
544
|
+
"context": context,
|
|
545
|
+
"class_context": class_context,
|
|
546
|
+
"lang": self.language_name,
|
|
547
|
+
"is_dependency": False,
|
|
548
|
+
}
|
|
549
|
+
variables.append(variable_data)
|
|
550
|
+
return variables
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def pre_scan_javascript(files: list[Path], parser_wrapper) -> dict:
|
|
554
|
+
"""Scans JavaScript files to create a map of class/function names to their file paths."""
|
|
555
|
+
imports_map = {}
|
|
556
|
+
query_str = """
|
|
557
|
+
(class_declaration name: (identifier) @name)
|
|
558
|
+
(function_declaration name: (identifier) @name)
|
|
559
|
+
(variable_declarator name: (identifier) @name value: (function_expression))
|
|
560
|
+
(variable_declarator name: (identifier) @name value: (arrow_function))
|
|
561
|
+
(method_definition name: (property_identifier) @name)
|
|
562
|
+
(assignment_expression
|
|
563
|
+
left: (member_expression
|
|
564
|
+
property: (property_identifier) @name
|
|
565
|
+
)
|
|
566
|
+
right: (function_expression)
|
|
567
|
+
)
|
|
568
|
+
(assignment_expression
|
|
569
|
+
left: (member_expression
|
|
570
|
+
property: (property_identifier) @name
|
|
571
|
+
)
|
|
572
|
+
right: (arrow_function)
|
|
573
|
+
)
|
|
574
|
+
"""
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
for path in files:
|
|
578
|
+
try:
|
|
579
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
580
|
+
tree = parser_wrapper.parser.parse(bytes(f.read(), "utf8"))
|
|
581
|
+
|
|
582
|
+
for capture, _ in execute_query(parser_wrapper.language, query_str, tree.root_node):
|
|
583
|
+
name = capture.text.decode('utf-8')
|
|
584
|
+
if name not in imports_map:
|
|
585
|
+
imports_map[name] = []
|
|
586
|
+
imports_map[name].append(str(path.resolve()))
|
|
587
|
+
except Exception as e:
|
|
588
|
+
warning_logger(f"Tree-sitter pre-scan failed for {path}: {e}")
|
|
589
|
+
return imports_map
|