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,499 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, Optional, Tuple
|
|
3
|
+
from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger
|
|
4
|
+
from codegraphcontext.utils.tree_sitter_manager import execute_query
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
GO_QUERIES = {
|
|
8
|
+
"functions": """
|
|
9
|
+
(function_declaration
|
|
10
|
+
name: (identifier) @name
|
|
11
|
+
parameters: (parameter_list) @params
|
|
12
|
+
) @function_node
|
|
13
|
+
|
|
14
|
+
(method_declaration
|
|
15
|
+
receiver: (parameter_list) @receiver
|
|
16
|
+
name: (field_identifier) @name
|
|
17
|
+
parameters: (parameter_list) @params
|
|
18
|
+
) @function_node
|
|
19
|
+
""",
|
|
20
|
+
"structs": """
|
|
21
|
+
(type_declaration
|
|
22
|
+
(type_spec
|
|
23
|
+
name: (type_identifier) @name
|
|
24
|
+
type: (struct_type) @struct_body
|
|
25
|
+
)
|
|
26
|
+
) @struct_node
|
|
27
|
+
""",
|
|
28
|
+
"interfaces": """
|
|
29
|
+
(type_declaration
|
|
30
|
+
(type_spec
|
|
31
|
+
name: (type_identifier) @name
|
|
32
|
+
type: (interface_type) @interface_body
|
|
33
|
+
)
|
|
34
|
+
) @interface_node
|
|
35
|
+
""",
|
|
36
|
+
"imports": """
|
|
37
|
+
(import_spec
|
|
38
|
+
path: (interpreted_string_literal) @path
|
|
39
|
+
)
|
|
40
|
+
""",
|
|
41
|
+
"calls": """
|
|
42
|
+
(call_expression
|
|
43
|
+
function: (identifier) @name
|
|
44
|
+
)
|
|
45
|
+
(call_expression
|
|
46
|
+
function: (selector_expression
|
|
47
|
+
field: (field_identifier) @name
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
""",
|
|
51
|
+
"variables": """
|
|
52
|
+
(var_declaration
|
|
53
|
+
(var_spec
|
|
54
|
+
name: (identifier) @name
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
(short_var_declaration
|
|
58
|
+
left: (expression_list
|
|
59
|
+
(identifier) @name
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
""",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
class GoTreeSitterParser:
|
|
66
|
+
"""A Go-specific parser using tree-sitter, encapsulating language-specific logic."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, generic_parser_wrapper):
|
|
69
|
+
self.generic_parser_wrapper = generic_parser_wrapper
|
|
70
|
+
self.language_name = generic_parser_wrapper.language_name
|
|
71
|
+
self.language = generic_parser_wrapper.language
|
|
72
|
+
self.parser = generic_parser_wrapper.parser
|
|
73
|
+
self.index_source = False
|
|
74
|
+
|
|
75
|
+
def _get_node_text(self, node) -> str:
|
|
76
|
+
return node.text.decode('utf-8')
|
|
77
|
+
|
|
78
|
+
def _get_parent_context(self, node, types=('function_declaration', 'method_declaration', 'type_declaration')):
|
|
79
|
+
curr = node.parent
|
|
80
|
+
while curr:
|
|
81
|
+
if curr.type in types:
|
|
82
|
+
if curr.type == 'type_declaration':
|
|
83
|
+
type_spec = curr.child_by_field_name('type_spec')
|
|
84
|
+
if type_spec:
|
|
85
|
+
name_node = type_spec.child_by_field_name('name')
|
|
86
|
+
return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
|
|
87
|
+
else:
|
|
88
|
+
name_node = curr.child_by_field_name('name')
|
|
89
|
+
return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
|
|
90
|
+
curr = curr.parent
|
|
91
|
+
return None, None, None
|
|
92
|
+
|
|
93
|
+
def _calculate_complexity(self, node):
|
|
94
|
+
"""
|
|
95
|
+
Compute a simple cyclomatic complexity score from the Go AST.
|
|
96
|
+
|
|
97
|
+
We treat each decision/control-flow construct as +1:
|
|
98
|
+
- if/for/switch/select
|
|
99
|
+
- switch cases (case_clause) and select clauses (comm_clause)
|
|
100
|
+
- logical operators (&&, ||) inside binary_expression as +1
|
|
101
|
+
"""
|
|
102
|
+
# Note: tree-sitter-go node types differ from other languages and from
|
|
103
|
+
# what we'd typically expect (e.g. it's `switch`/`case` rather than
|
|
104
|
+
# `switch_statement`/`case_clause`).
|
|
105
|
+
decision_node_types = {
|
|
106
|
+
# top-level control constructs
|
|
107
|
+
"if_statement",
|
|
108
|
+
"for_statement",
|
|
109
|
+
"switch",
|
|
110
|
+
"select_statement",
|
|
111
|
+
"select",
|
|
112
|
+
# switch variants (tree-sitter grammar)
|
|
113
|
+
"expression_switch_statement",
|
|
114
|
+
"type_switch_statement",
|
|
115
|
+
# switch case branches
|
|
116
|
+
"case",
|
|
117
|
+
"expression_case",
|
|
118
|
+
"default_case",
|
|
119
|
+
# select communication branches
|
|
120
|
+
"communication_case",
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
count = 1
|
|
124
|
+
|
|
125
|
+
def traverse(n):
|
|
126
|
+
nonlocal count
|
|
127
|
+
if n.type in decision_node_types:
|
|
128
|
+
count += 1
|
|
129
|
+
# Still traverse children because nested constructs also contribute.
|
|
130
|
+
elif n.type == "binary_expression":
|
|
131
|
+
# Only count logical operators, not all binary expressions/comparisons.
|
|
132
|
+
# Example patterns: "a && b", "a || b".
|
|
133
|
+
try:
|
|
134
|
+
txt = self._get_node_text(n)
|
|
135
|
+
except Exception:
|
|
136
|
+
txt = ""
|
|
137
|
+
if "&&" in txt or "||" in txt:
|
|
138
|
+
count += 1
|
|
139
|
+
|
|
140
|
+
for child in n.children:
|
|
141
|
+
traverse(child)
|
|
142
|
+
|
|
143
|
+
traverse(node)
|
|
144
|
+
return count
|
|
145
|
+
|
|
146
|
+
def _get_docstring(self, func_node):
|
|
147
|
+
"""Extract Go doc comment preceding the function."""
|
|
148
|
+
prev_sibling = func_node.prev_sibling
|
|
149
|
+
while prev_sibling and prev_sibling.type in ('comment', '\n', ' '):
|
|
150
|
+
if prev_sibling.type == 'comment':
|
|
151
|
+
comment_text = self._get_node_text(prev_sibling)
|
|
152
|
+
if comment_text.startswith('//'):
|
|
153
|
+
return comment_text.strip()
|
|
154
|
+
prev_sibling = prev_sibling.prev_sibling
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False) -> Dict:
|
|
158
|
+
"""Parses a file and returns its structure in a standardized dictionary format."""
|
|
159
|
+
# This method orchestrates the parsing of a single file.
|
|
160
|
+
# It calls specialized `_find_*` methods for each language construct.
|
|
161
|
+
# The returned dictionary should map a specific key (e.g., 'functions', 'interfaces')
|
|
162
|
+
# to a list of dictionaries, where each dictionary represents a single code construct.
|
|
163
|
+
# The GraphBuilder will then use these keys to create nodes with corresponding labels.
|
|
164
|
+
self.index_source = index_source
|
|
165
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
166
|
+
source_code = f.read()
|
|
167
|
+
|
|
168
|
+
tree = self.parser.parse(bytes(source_code, "utf8"))
|
|
169
|
+
root_node = tree.root_node
|
|
170
|
+
|
|
171
|
+
functions = self._find_functions(root_node)
|
|
172
|
+
structs = self._find_structs(root_node)
|
|
173
|
+
interfaces = self._find_interfaces(root_node)
|
|
174
|
+
imports = self._find_imports(root_node)
|
|
175
|
+
function_calls = self._find_calls(root_node)
|
|
176
|
+
variables = self._find_variables(root_node)
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
"path": str(path),
|
|
180
|
+
"functions": functions,
|
|
181
|
+
"classes": structs,
|
|
182
|
+
"interfaces": interfaces,
|
|
183
|
+
"variables": variables,
|
|
184
|
+
"imports": imports,
|
|
185
|
+
"function_calls": function_calls,
|
|
186
|
+
"is_dependency": is_dependency,
|
|
187
|
+
"lang": self.language_name,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
def _find_functions(self, root_node):
|
|
191
|
+
functions = []
|
|
192
|
+
query_str = GO_QUERIES['functions']
|
|
193
|
+
|
|
194
|
+
captures_by_function = {}
|
|
195
|
+
|
|
196
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
197
|
+
if capture_name == 'function_node':
|
|
198
|
+
func_id = node.id
|
|
199
|
+
if func_id not in captures_by_function:
|
|
200
|
+
captures_by_function[func_id] = {
|
|
201
|
+
'node': node,
|
|
202
|
+
'name': None,
|
|
203
|
+
'params': None,
|
|
204
|
+
'receiver': None
|
|
205
|
+
}
|
|
206
|
+
elif capture_name == 'name':
|
|
207
|
+
func_node = self._find_function_node_for_name(node)
|
|
208
|
+
if func_node:
|
|
209
|
+
func_id = func_node.id
|
|
210
|
+
if func_id not in captures_by_function:
|
|
211
|
+
captures_by_function[func_id] = {
|
|
212
|
+
'node': func_node,
|
|
213
|
+
'name': None,
|
|
214
|
+
'params': None,
|
|
215
|
+
'receiver': None
|
|
216
|
+
}
|
|
217
|
+
captures_by_function[func_id]['name'] = self._get_node_text(node)
|
|
218
|
+
elif capture_name == 'params':
|
|
219
|
+
func_node = self._find_function_node_for_params(node)
|
|
220
|
+
if func_node:
|
|
221
|
+
func_id = func_node.id
|
|
222
|
+
if func_id not in captures_by_function:
|
|
223
|
+
captures_by_function[func_id] = {
|
|
224
|
+
'node': func_node,
|
|
225
|
+
'name': None,
|
|
226
|
+
'params': None,
|
|
227
|
+
'receiver': None
|
|
228
|
+
}
|
|
229
|
+
captures_by_function[func_id]['params'] = node
|
|
230
|
+
elif capture_name == 'receiver':
|
|
231
|
+
func_node = node.parent
|
|
232
|
+
if func_node and func_node.type == 'method_declaration':
|
|
233
|
+
func_id = func_node.id
|
|
234
|
+
if func_id not in captures_by_function:
|
|
235
|
+
captures_by_function[func_id] = {
|
|
236
|
+
'node': func_node,
|
|
237
|
+
'name': None,
|
|
238
|
+
'params': None,
|
|
239
|
+
'receiver': None
|
|
240
|
+
}
|
|
241
|
+
captures_by_function[func_id]['receiver'] = node
|
|
242
|
+
|
|
243
|
+
for func_id, data in captures_by_function.items():
|
|
244
|
+
if data['name']:
|
|
245
|
+
func_node = data['node']
|
|
246
|
+
name = data['name']
|
|
247
|
+
|
|
248
|
+
args = []
|
|
249
|
+
if data['params']:
|
|
250
|
+
args = self._extract_parameters(data['params'])
|
|
251
|
+
|
|
252
|
+
receiver_type = None
|
|
253
|
+
if data['receiver']:
|
|
254
|
+
receiver_type = self._extract_receiver(data['receiver'])
|
|
255
|
+
|
|
256
|
+
context, context_type, context_line = self._get_parent_context(func_node)
|
|
257
|
+
class_context = receiver_type or (context if context_type == 'type_declaration' else None)
|
|
258
|
+
|
|
259
|
+
docstring = self._get_docstring(func_node)
|
|
260
|
+
|
|
261
|
+
func_data = {
|
|
262
|
+
"name": name,
|
|
263
|
+
"line_number": func_node.start_point[0] + 1,
|
|
264
|
+
"end_line": func_node.end_point[0] + 1,
|
|
265
|
+
"args": args,
|
|
266
|
+
"class_context": class_context,
|
|
267
|
+
"decorators": [],
|
|
268
|
+
"lang": self.language_name,
|
|
269
|
+
"is_dependency": False,
|
|
270
|
+
"cyclomatic_complexity": self._calculate_complexity(func_node),
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if self.index_source:
|
|
274
|
+
func_data["source"] = self._get_node_text(func_node)
|
|
275
|
+
func_data["docstring"] = docstring
|
|
276
|
+
|
|
277
|
+
functions.append(func_data)
|
|
278
|
+
|
|
279
|
+
return functions
|
|
280
|
+
|
|
281
|
+
def _find_function_node_for_name(self, name_node):
|
|
282
|
+
current = name_node.parent
|
|
283
|
+
while current:
|
|
284
|
+
if current.type in ('function_declaration', 'method_declaration'):
|
|
285
|
+
return current
|
|
286
|
+
current = current.parent
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
def _find_function_node_for_params(self, params_node):
|
|
290
|
+
current = params_node.parent
|
|
291
|
+
while current:
|
|
292
|
+
if current.type in ('function_declaration', 'method_declaration'):
|
|
293
|
+
return current
|
|
294
|
+
current = current.parent
|
|
295
|
+
return None
|
|
296
|
+
|
|
297
|
+
def _extract_parameters(self, params_node):
|
|
298
|
+
params = []
|
|
299
|
+
if params_node.type == 'parameter_list':
|
|
300
|
+
for child in params_node.children:
|
|
301
|
+
if child.type == 'parameter_declaration':
|
|
302
|
+
# Handle multiple names for same type: func(x, y int)
|
|
303
|
+
# We iterate children and find all identifiers that are not the type node.
|
|
304
|
+
type_node = child.child_by_field_name('type')
|
|
305
|
+
for grandchild in child.children:
|
|
306
|
+
if grandchild.type == 'identifier':
|
|
307
|
+
if grandchild.id != (type_node.id if type_node else None):
|
|
308
|
+
params.append(self._get_node_text(grandchild))
|
|
309
|
+
elif child.type == 'variadic_parameter_declaration':
|
|
310
|
+
name_node = child.child_by_field_name('name')
|
|
311
|
+
if name_node:
|
|
312
|
+
params.append(f"...{self._get_node_text(name_node)}")
|
|
313
|
+
return params
|
|
314
|
+
|
|
315
|
+
def _extract_receiver(self, receiver_node):
|
|
316
|
+
if receiver_node.type == 'parameter_list' and receiver_node.named_child_count > 0:
|
|
317
|
+
param = receiver_node.named_child(0)
|
|
318
|
+
type_node = param.child_by_field_name('type')
|
|
319
|
+
if type_node:
|
|
320
|
+
type_text = self._get_node_text(type_node)
|
|
321
|
+
return type_text.strip('*')
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
def _find_structs(self, root_node):
|
|
325
|
+
structs = []
|
|
326
|
+
struct_query_str = GO_QUERIES['structs']
|
|
327
|
+
for node, capture_name in execute_query(self.language, struct_query_str, root_node):
|
|
328
|
+
if capture_name == 'name':
|
|
329
|
+
struct_node = self._find_type_declaration_for_name(node)
|
|
330
|
+
if struct_node:
|
|
331
|
+
name = self._get_node_text(node)
|
|
332
|
+
class_data = {
|
|
333
|
+
"name": name,
|
|
334
|
+
"line_number": struct_node.start_point[0] + 1,
|
|
335
|
+
"end_line": struct_node.end_point[0] + 1,
|
|
336
|
+
"bases": [],
|
|
337
|
+
"decorators": [],
|
|
338
|
+
"lang": self.language_name,
|
|
339
|
+
"is_dependency": False,
|
|
340
|
+
}
|
|
341
|
+
if self.index_source:
|
|
342
|
+
class_data["source"] = self._get_node_text(struct_node)
|
|
343
|
+
class_data["docstring"] = self._get_docstring(struct_node)
|
|
344
|
+
|
|
345
|
+
structs.append(class_data)
|
|
346
|
+
return structs
|
|
347
|
+
|
|
348
|
+
def _find_interfaces(self, root_node):
|
|
349
|
+
interfaces = []
|
|
350
|
+
interface_query_str = GO_QUERIES['interfaces']
|
|
351
|
+
for node, capture_name in execute_query(self.language, interface_query_str, root_node):
|
|
352
|
+
if capture_name == 'name':
|
|
353
|
+
interface_node = self._find_type_declaration_for_name(node)
|
|
354
|
+
if interface_node:
|
|
355
|
+
name = self._get_node_text(node)
|
|
356
|
+
class_data = {
|
|
357
|
+
"name": name,
|
|
358
|
+
"line_number": interface_node.start_point[0] + 1,
|
|
359
|
+
"end_line": interface_node.end_point[0] + 1,
|
|
360
|
+
"bases": [],
|
|
361
|
+
"decorators": [],
|
|
362
|
+
"lang": self.language_name,
|
|
363
|
+
"is_dependency": False,
|
|
364
|
+
}
|
|
365
|
+
if self.index_source:
|
|
366
|
+
class_data["source"] = self._get_node_text(interface_node)
|
|
367
|
+
class_data["docstring"] = self._get_docstring(interface_node)
|
|
368
|
+
|
|
369
|
+
interfaces.append(class_data)
|
|
370
|
+
return interfaces
|
|
371
|
+
|
|
372
|
+
def _find_type_declaration_for_name(self, name_node):
|
|
373
|
+
current = name_node.parent
|
|
374
|
+
while current:
|
|
375
|
+
if current.type == 'type_declaration':
|
|
376
|
+
return current
|
|
377
|
+
current = current.parent
|
|
378
|
+
return None
|
|
379
|
+
|
|
380
|
+
def _find_imports(self, root_node):
|
|
381
|
+
imports = []
|
|
382
|
+
query_str = GO_QUERIES['imports']
|
|
383
|
+
|
|
384
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
385
|
+
line_number = node.start_point[0] + 1
|
|
386
|
+
|
|
387
|
+
if capture_name == 'path':
|
|
388
|
+
path_text = self._get_node_text(node).strip('"')
|
|
389
|
+
package_name = path_text.split('/')[-1]
|
|
390
|
+
|
|
391
|
+
alias = None
|
|
392
|
+
import_spec = node.parent
|
|
393
|
+
if import_spec and import_spec.type == 'import_spec':
|
|
394
|
+
alias_node = import_spec.child_by_field_name('name')
|
|
395
|
+
if alias_node:
|
|
396
|
+
alias = self._get_node_text(alias_node)
|
|
397
|
+
|
|
398
|
+
imports.append({
|
|
399
|
+
'name': package_name,
|
|
400
|
+
'source': path_text,
|
|
401
|
+
'alias': alias,
|
|
402
|
+
'line_number': line_number,
|
|
403
|
+
'lang': self.language_name
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
return imports
|
|
407
|
+
|
|
408
|
+
def _find_calls(self, root_node):
|
|
409
|
+
calls = []
|
|
410
|
+
query_str = GO_QUERIES['calls']
|
|
411
|
+
|
|
412
|
+
seen_calls = set()
|
|
413
|
+
|
|
414
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
415
|
+
if capture_name == 'name':
|
|
416
|
+
call_node = node.parent
|
|
417
|
+
while call_node and call_node.type != 'call_expression':
|
|
418
|
+
call_node = call_node.parent
|
|
419
|
+
|
|
420
|
+
if call_node:
|
|
421
|
+
name = self._get_node_text(node)
|
|
422
|
+
line_number = node.start_point[0] + 1
|
|
423
|
+
|
|
424
|
+
call_key = f"{name}_{line_number}"
|
|
425
|
+
if call_key in seen_calls:
|
|
426
|
+
continue
|
|
427
|
+
seen_calls.add(call_key)
|
|
428
|
+
|
|
429
|
+
full_name = self._get_node_text(call_node.child_by_field_name('function')) if call_node.child_by_field_name('function') else name
|
|
430
|
+
|
|
431
|
+
# Resolve context
|
|
432
|
+
context_name, context_type, context_line = self._get_parent_context(node)
|
|
433
|
+
|
|
434
|
+
# In Go, methods are defined on types (structs/interfaces). If we are in a method, the context is the method name.
|
|
435
|
+
# Ideally we might want the receiver type as "class_context", but this requires more complex AST traversal up to the method declaration's receiver.
|
|
436
|
+
# For now, we reuse the context resolution logic.
|
|
437
|
+
class_context = None
|
|
438
|
+
|
|
439
|
+
call_data = {
|
|
440
|
+
"name": name,
|
|
441
|
+
"full_name": full_name,
|
|
442
|
+
"line_number": line_number,
|
|
443
|
+
"args": [],
|
|
444
|
+
"inferred_obj_type": None,
|
|
445
|
+
"context": (context_name, context_type, context_line),
|
|
446
|
+
"class_context": class_context,
|
|
447
|
+
"lang": self.language_name,
|
|
448
|
+
"is_dependency": False,
|
|
449
|
+
}
|
|
450
|
+
calls.append(call_data)
|
|
451
|
+
|
|
452
|
+
return calls
|
|
453
|
+
|
|
454
|
+
def _find_variables(self, root_node):
|
|
455
|
+
variables = []
|
|
456
|
+
query_str = GO_QUERIES['variables']
|
|
457
|
+
|
|
458
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
459
|
+
if capture_name == 'name':
|
|
460
|
+
name = self._get_node_text(node)
|
|
461
|
+
|
|
462
|
+
variable_data = {
|
|
463
|
+
"name": name,
|
|
464
|
+
"line_number": node.start_point[0] + 1,
|
|
465
|
+
"value": None,
|
|
466
|
+
"type": None,
|
|
467
|
+
"context": None,
|
|
468
|
+
"class_context": None,
|
|
469
|
+
"lang": self.language_name,
|
|
470
|
+
"is_dependency": False,
|
|
471
|
+
}
|
|
472
|
+
variables.append(variable_data)
|
|
473
|
+
|
|
474
|
+
return variables
|
|
475
|
+
|
|
476
|
+
def pre_scan_go(files: list[Path], parser_wrapper) -> dict:
|
|
477
|
+
"""Scans Go files to create a map of function/struct names to their file paths."""
|
|
478
|
+
imports_map = {}
|
|
479
|
+
query_str = """
|
|
480
|
+
(function_declaration name: (identifier) @name)
|
|
481
|
+
(method_declaration name: (field_identifier) @name)
|
|
482
|
+
(type_declaration (type_spec name: (type_identifier) @name))
|
|
483
|
+
"""
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
for path in files:
|
|
487
|
+
try:
|
|
488
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
489
|
+
tree = parser_wrapper.parser.parse(bytes(f.read(), "utf8"))
|
|
490
|
+
|
|
491
|
+
for capture, _ in execute_query(parser_wrapper.language, query_str, tree.root_node):
|
|
492
|
+
name = capture.text.decode('utf-8')
|
|
493
|
+
if name not in imports_map:
|
|
494
|
+
imports_map[name] = []
|
|
495
|
+
imports_map[name].append(str(path.resolve()))
|
|
496
|
+
except Exception as e:
|
|
497
|
+
warning_logger(f"Tree-sitter pre-scan failed for {path}: {e}")
|
|
498
|
+
|
|
499
|
+
return imports_map
|